Fork me on GitHub

Google Code Prettify Nasıl Kullanarak Kod Renklendirme Nasıl Yapılır?

19 May 2012 Mirat Can Bayrak

Malum bir python programcıları olarak bir gezegenimiz var artık. Ne mutlu ki katılımcı olmak isteyen arkadaşlar gelmeye de başladılar. Ancak bir iki arkadaşı geri çevirmek zorunda kaldık zira onlardan ufak bir isteğimiz var. Kod bloklarını google-code-prettify ile renklendirmelerini istiyoruz. Bunun sebebi farklı farklı kod renklendirme sistemi ile gelen yazıları gezegen içerisinde düzgün gösteremiyor olmamız. Gezegen içerisinde google-code-prettify çalışıyor. Bu güzel aletin özellikleri ise şöyle:

  • Her hangi bir dil tanımlamasına ihtiyaç duymuyor, dilin ne olduğunu kendisi anlayıp renklendirmeyi yapıyor
  • Kod bloğu çeşitli sayılar, satır numaralarına sahip olsa da çalışıyor
  • Hafif, dosya boyutu küçük ve render işlemi sırasında browser'ı yormuyor
  • CSS kullanılarak temalandırılabiliyor
  • C, Bash, Xml benzeri kodları da renlendirebiliyor

Kullanımı ise çok basit, eğer inline bir kod verecekseniz örneğin aşağıdaki gibi bir cümle kuracak iseniz:

Bir listeyi oluşturmak için değişkenin karşısına liste elemanlarını sepet = ["elma", "armut", "cilek"] gibi köşeli parantez içerisine virgülle ayırarak vermeniz yeterlidir.

Sadece cümlede kod olarak belirtmek istediğiniz yeri "code" etiketi altıa alın ve sınıf ismi olarak "prettyprint" verin.

Eğer inline değil blok olarak kod verecekseniz örneğin aşağıdaki gibi bir şey çıkarmak istiyorsanız:

from os import uname, getcwd
from os.path import join
MACHINE_NAME = uname()[1]

if MACHINE_NAME == "XXX.webfaction.com":
DOCUMENT_ROOT = "/path/to/document/"
else:
DOCUMENT_ROOT = getcwd()
DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = join(DOCUMENT_ROOT,'sqlite3.db')

yine prettyprint sınıfı ile ancak bu sefer pre etiketi ile çerçeveleyerek bloğumuzu veriyoruz.

Yazıyı yazarken kodun gezegen içerisinde düzgün çıkması için yapmamız gerekenler sadece bu ikisi. Kendi blogunuzda çalışmasını sağlamak için ise ilgili js dosyasını ve tema galerisinden seçtiğiniz bir css dosyasını sayfa başında implemente ettikten sonra sayfanızın body etiketine onLoad="PrettyPrint()" parametresini vermeniz yetiyor.

Ayrıca isterseniz gezegenin html kodunu inceleyebilirsiniz.

Django'da iki sunucuda birden çalışmak ve settings.py

19 May 2012 Mirat Can Bayrak
Şu sıralar uğraştığım bir Django uygulaması var geliştirirken karşılaştığım bir sorunu ve çözümünü paylaşayım dedim.

Geliştirdiğim uygulamayı sunucuya yükledikten sonra lokalde geliştirirken oluşturduğum bir çok seçeneğin sunucudayken değişmesi gerektiğini farkettim. Bu ayarları değiştirmek kolaydı fakat sıkıntı çektiğim nokta uygulamanın hem sunucuda hemde kendi laptopumda tek bir settings.py ile sorunsuzca çalışması gerekiyordu. Bende şöyle bir çözüm buldum :

from os import uname, getcwd
from os.path import join
MACHINE_NAME = uname()[1]

if MACHINE_NAME == "XXX.webfaction.com":
DOCUMENT_ROOT = "/path/to/document/"
DATABASE_ENGINE = ...
DATABASE_NAME = ...
DATABASE_USER = ...
DATABASE_PASSWORD = ...
DATABASE_HOST = ...
DATABASE_PORT = ...
else:
DOCUMENT_ROOT = getcwd()
DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = join(DOCUMENT_ROOT,'sqlite3.db')

Python'da listenin kopyasını almak

19 May 2012 Mirat Can Bayrak

Python da her eşitlik referans gösterir. Bu iddalı lafın anlamını şöyle ifade edeyim.
Örneğin şekildeki gibi bir liste = başka bir liste şeklinde kopya almaya çalıştığınızda şöyle bir gariplik (aslında güzellik) ile karşılaşırsınız.

a = ["a","b","c"]
>>> b = a
>>> b.append("d")
>>> print a, b
['a', 'b', 'c', 'd'] ['a', 'b', 'c', 'd']
Nesnelere gelince yine aynı mantık, nesne = başkabirnesne dediğinizde referans göstermiş oluyorsrunuz :

>> class obj:
... def __init__(self):
... self.counter = 0
... def inc(self):
... self.counter += 1
...
>>> a = obj()
>>> b = a
>>> a.inc()
>>> print a.counter
1
>>> print b.counter
1
Bir nesnenin ya da değişkenin kopyasını almak istediğinizde çeşitli yöntemler var, sanırım verilerin (list, tuple, dict vs) kopyasını alırken karşılaştığım en yakışıklı yöntem şu :

>>> a = ["a","b","c"]
>>> b = list(a)
>>> print a , b
['a', 'b', 'c'] ['a', 'b', 'c']
>>> a.append("d")
>>> print a, b
['a', 'b', 'c', 'd'] ['a', 'b', 'c']

Peki bir nesnenin kopyasını almak istiyorsak? bunun için copy adında bir modül yapmışlar:

>>> class obj:
... def __init__(self):
... self.counter = 0
... def inc(self):
... self.counter += 1
...
>>> a = obj()
>>> from copy import copy
>>> b = copy(a)
>>> a.inc()
>>> b.inc()
>>> b.inc()
>>> b.inc()
>>> print a.counter, b.counter
1 3

Eğer copy modülünden deepcopy fonksionunu import eder kullanırsak, nesneyi özyinelemeli olarak kopyalıyormuş. Herkese iyi hafta sonları :)

Migrating from pivotaltracker to Github.

19 May 2012 Mirat Can Bayrak

Çalıştığım projede iş takip sistemi olarak pivotaltracker kullanıyorduk. Daha sonra GitHub'a taşınma kararı aldık. Bu taşınma işlemini otomatik olarak gerçekleştirebilecek bir script bulamadım. Bende kendim yazmaya karar verdim. Size de birgün lazm olabilir. Buyrun kullanın.

Currently i needed to migrate from pivotaltracker to github. I could'nt find any automated solution for that. So i wrote my solution in Python. Use freely when you need it. :)

from github2.client import Github
from csv import reader as CsvReader

# FILL INFORMATION BELOW

# your username at github
GITHUB_USERNAME = ""

# your api token, you can find it at https://github.com/account/admin
GITHUB_API_TOKEN = ""

# path to file that you exported from pivotal tracker
PROJECT_CSV_PATH = ""

# project path at github like: "user/projectname"
GITHUB_PROJECT_PATH = ""

github = Github(
username=GITHUB_USERNAME,
api_token=GITHUB_API_TOKEN,
request_per_second=1)

reader = CsvReader(open(PROJECT_CSV_PATH, "r"), delimiter=",")

for row in reader:
print "title :", row[1]
print "description :", row[14]
print "--------"
github.issues.open(GITHUB_PROJECT_PATH, title=row[1], body=row[14])

Tornado'mu Node.js mi dedim kendi kendime...

19 May 2012 Mirat Can Bayrak

Yeni ve artık son okul dönemi ile birlikte Lookremix projesinden ayrılmak zorunda kaldım. Bir startup içerisinde bulunmak oldukça eğitici oldu benim için. Hem bir sosyal medya aracının oluşturulurken ne gibi badireler atlattığını görmüş oldum. Hem de bir yazılım geliştirici olarak daha önce hiç el atmadığım meselelere el atmak durumunda kaldım. Bunun yanında venezuelalı kadınların nasıl çekilmez olabileceğini devamlı sizle nasıl dalga geçmeyi başarabileceğini öğrenmiş oldum :P. Benim için oldukça çetrefilli ama bir yandan da oldukça zevkli bir iş oldu. Keşke daha fazla devam edebilseydim diyorum ama belli olmaz belki ileride yollarımız tekrar kesişir.

Şu anda ise ilk göz ağrım bilsin'i tekrar ayağa kaldırmak için elime bir fırsat geçti. Geçerli sosyal medya araçlarının zayıflığı ve bilsin'in neden güçlü bir araç olacağı konusunda okuldaki jüriye yaptığım sunum oldukça ikna edici oldu ve şu bilsin resmi olarak bitirme projem haline dönüştü. Ancak çok çalışmam gerek çünkü şu anda her ne kadar kırık dökük de olsa çalışan bir alet (tool) görünümü verse de aslında kafamdaki ideolojiden ve sağlamlıktan uzak durumda.

Projeye daha önce başlarken yaptığım yanlış teknoloji seçimlerini ve kervan yolda düzülür diyip yolda yol değiştirmeleri (gerçekten çok fazla enerji ve kaybı oluyor) tekrar yaşamamak için mümkün olduğunca belgeli, planlı davranmaya çalışıyor. Çıkacak sorunları mümkün olduğunca ön görmeye çalışıyorum. Blog üzerinden çok fazla ayrıntı vermeyi uygun görmüyorum ama projenin %90'ının sağlam restFul api oluşturmaktan geçtiğini ve mümkün olduğunca akışkan bir yapıda olacağını söyleyebilirim. Tabi bu noktada Django kullanmak bir az saçma oluyor zira api yazmak için context processorlere ya da template sistemine ihtiyaç duymuyorum. Bunun yanında Django ile long pooling yapmak ya da websockets kullanmak deveyi at yarışına sokmak gibi bir şey olurdu.

Bu noktada Node.js gel beni kullan diye göz kırpıyor, diğer kolumda da Tornado var. Bunları kendimce karşılaştırmaya çalıştım ancak eğer yanlış bilgi verdiğimi, yanıldığımı düşünüyorsanız lütfen yorum bırakın.

Performans

Node.js Google beyin V8 adlı javascript motorunu kullanıyor. Javascript kodları derlenip makina diline çevriliyor. Bu da kallavi bir hız demek. Tornado ise bunu ta ki Python 3.3 çıkana kadar yapamayacak(mış). İzlediğim node.js sunumunda yapılan testte ki bu test sadece bir "hello world" testi. Node.js saniyede 4340 cevap verebilirken tornado ise sadece 2344 cevap verebilmiş. Sözün özü Node.js performans konusunda Tornado'yu ikiye katlıyor.

Dil Yapısı

Bu noktada Tornado golünü atıyor zira hiç bir programcının Python varken Javascript kullanmak isteyeceğini sanmıyorum. Javascript'in iğrenç bir syntax yapısı var. Bu Python sever birinin şahsi fikri olmakla beraber, pek çok kişinin de noktalı virgül ya da memeli parantezler ile boğuşmak isteyeceğini sanmıyorum. Hoş, syntax olayı CofeeScript sayesinde çözülebiliyor ancak yine de dahili standartlaşmış kütüphanelerin eksikliği hissediliyor. Bir each döngüsü yaratmak için bile harici bir kütüphaneye ihtiyaç duymak, bende huzursuzluğa yol açıyor. Yengeç burcu programcılar için önermiyorum. :)

İkinci olarak javascript ilk defa browser dışında bir mecrada iş görmeye başladığı için bir çok kütüphane olgunlaşmamış durumda. Halbu ki Python 1994 de 1.0 versiyonuna ulaştı yıllardır her konuda kütüphane geliştirilmekte kendisi için. Node.js nin şu anki versiyonu 0.4.1 Tornado'nun ise 2.1.1.

Üçüncü olarak, Javascript in doğal halinin etkinlik tabanlı (event-driven) bir dil olduğunu bu yüzden geliştirilen kütüphanelerin haliyle asenkron kütüphaneler olduğunu. Python ise aslında sıralı (satır satır) işlenen bir dil olması itibari ile Tornado ile kullanacağınız kütüphanelerin özel olarak asenkron olarak yazılmış kütüphaneler olması gerektiğini eklemem gerekiyor. Anlayacağınız Tornado içinde mysqllib'i direkt olarak import edip kullanamıyorsunuz. Bunun yerine asenkron çalışacak şekilde tasarlanmış mysql kütüphanesi ne ise onu bulup kullanmanız gerekiyor.

Ancak ilginçtir ki her ne kadar Tornado daha olgun olsa da içerisinde bir session management olayı bulundurmuyor. Bu bana çok garip geldi. Sanırım Django'da ki:

if user.is_authenticated():
...
...

Türevi sorular soramayacağımız manasına geliyor Tornado'ya. Belki authentication olayları başka türlü cookie tabanlı atraksiyonlarla (OAuth?) çözülüyordur bilemiyorum.

Topluluk

Node.js'in çok heyecanlı bir topluluk henüz bir velet olmasına rağmen inanılmaz ilgi görüyor. Aşağıdaki bu rakamlar söylediğimi kanıtlar nitelikte: (Fork, Takipçi ve İlgili Depo sayılarını Github'dan aldım)

Fork Takipçi İlgili Depo Eposta Listesi
Tornado 510 3,313 364 1779
Node.js 1,339 11,944 5842 6590

Sonuç

Sonuç olarak Node.js topluluk ve performans olarak Tornado'yu dövüyor gözüküyor. Ancak sanırım geliştiriciler arasında bir garip moda ya da trendin peşinden hızla gitme durumu var. Yeni bir teknoloji afili bir sunumla anlatılıp da bir kaç yerde yazılıp çizildiği zaman hurra herkes o tarafa gidiyor. Sonra herkes sakinleşiyor hangi dili rahat kullanıyorsa onu kullanmaya devam ediyor. Bu yüzden bu 7000 kişilik e-posta listelerini falan biraz şişirilmiş rakamlar olarak görüyorum. Ayrıca bu devasa kitleden kaç tanesi gerçek bir iş görebilmek üzere sizin ofisinize gelip çalışabilecek insanlardır bunu hesap etmek gerek. üç mü beş mi?

Bunun yanında Node.js teoride bu kadar ilgi görmesine rağmen, henüz kendisini savaş arenasında görmüş değiliz. Gerçek - büyük projelerde ne gibi problemler çıkaracağı konusunda pek fikrimiz yok. Tornado ise halihazırda Facebook'un ve FriendFeed'in ağır işlerini gören bir sistem.

Bütün bunlara dayanarak, üzülerek Node.js ye bizimle değilsin diyorum. Zira saniyede 4bin request karşılayabiliyor olması bende yarattığı rahatsızlık hissini gidermeye yetmiyor. Zaten bir gün sunucularım altından kalkamayacağım bir yükle karşılaşırsa zaten para kazanıyorum demektir bir sunucu daha koyarım.

Kaynaklar:

Python'da Asal Sayı Bulma Algoritmaları ve Bunların Evrimi

23 December 2011 Yaşar Arabacı
Kimse inkar etmesin, herkes programlama öğrenme sürecinin bir aşamasında asal sayı hesaplamaya çalışmıştır. Bu ilk algoritmalar, çoğu zaman verimsiz algoritmalardır. Biraz kendi deneyimlerimden yola çıkarak, Python'dan asal sayı hesaplama fonksiyonlarının evrim sürecinden bahsedeceğim.

Tanımdan Gitme - Prosedürel

Asal sayıları biz birden ve kendinden başka bir sayıya kalansız bölünemeyen sayılar olarak biliriz. Bu doğru bir tanımdır. Programlama dilini yeni öğrenenler, ilk olarak tanımdan yola çıkan algoritmalar yazarlar.

# -*- coding:utf-8 -*-
# 1000 tane asal sayı bulan program

print 2
# xrange değil henüz...
for i in range(3,1000):

    bolundu = False
    for j in range(2,i):
            if i % j == 0:
                bolundu=True
                # break yok...
    if bolundu == False:
        print i

Tanımdan Gitme - Fonksiyonel

Asal sayı bulan ilk algoritmasını yazan programcılar, daha sonra bunu bir fonksiyon haline getirmeyi ve tekrar tekrar kullanabilmeyi düşünür. Aynı zamanda, birkaç önemli detay da algoritmaya eklenmiştir.

# -*- coding:utf-8 -*-

def asal(kaca_kadar):
    """Asal sayı bulan fonksiyon
    Girdi olarak bir sayı alır
    Bu sayıya kadar olan asal sayıları ekrana basar.
    """
    if kaca_kadar < 2:
        return
    elif kaca_kadar == 2:
        print 2
        return
    else:
        print 2
        for i in range(3,kaca_kadar):
            bolundu = False
            for j in range(2,i):
                if i % j == 0:
                    bolundu=True
                    break
            if bolundu == False:
                print i

Fonksiyondan Liste Döndürme

Fonksiyon içinden direk ekrana değer basma karşıtı görüşler okuyan programcı, fonksiyondan bir liste döndürür. Daha sonra listeyi ekrana basar.

# -*- coding:utf-8 -*-

def asal(kaca_kadar):
    """Asal sayı bulan fonksiyon
    Girdi olarak bir sayı alır
    Bu sayıya kadar olan asal sayıları ekrana basar.
    """
    asallar = [2]
    if kaca_kadar < 2:
        return None
    elif kaca_kadar == 2:
        return asallar
    else:
        for i in range(3,kaca_kadar):
            bolundu = False
            for j in range(2,i):
                if i % j == 0:
                    bolundu=True
                    break
            if bolundu == False:
                asallar.append(i)
    return asallar
    
if __name__ == "__main__":
    # join kullanmak için, listedekiler str olmalı.
    print "\n".join(map(str,asal(1000)))
    """
    Bu aşamada map() bilinmiyorsa, list comprehension veya for döngüsü kullanılır.
    print "\n".join([str(asal) for asal in asal(1000)])
    for asal in asal(1000)
        print asal
    """
    

Performans Kaygıları

Programcı artık algoritmasının performansını kafaya takacak aşamaya gelmiştir. Çift sayılar artık hesaplanmaz, sayılar yarısından büyük sayılarla bölünmeye çalışılmaz.

# -*- coding:utf-8 -*-

def asal(kaca_kadar):
    """Asal sayı bulan fonksiyon
    Girdi olarak bir sayı alır
    Bu sayıya kadar olan asal sayıları ekrana basar.
    """
    asallar = [2]
    if kaca_kadar < 2:
        return None
    elif kaca_kadar == 2:
        return asallar
    else:
        for i in xrange(3,kaca_kadar,2):
            bolundu = False
            for j in xrange(3,(i/2) + 1, 2):
                if i % j == 0:
                    bolundu=True
                    break
            if bolundu == False:
                asallar.append(i)
    return asallar
    
if __name__ == "__main__":
    # join kullanmak için, listedekiler str olmalı.
    print "\n".join(map(str,asal(10000)))
    """
    Bu aşamada map() bilinmiyorsa, list comprehension veya for döngüsü kullanılır.
    print "\n".join([str(asal) for asal in asal(1000)])
    for asal in asal(1000)
        print asal
    """
    

Sadece asallarla bölmeye çalışmak

Programcı, asal sayı bulmak için, o sayıyı sadece asal sayılarla bölmeye çalışmanın yettiğini hatırlar, bir önceki yöntemle bunu birleştirir.

# -*- coding:utf-8 -*-

def asal(kaca_kadar):
    """Asal sayı bulan fonksiyon
    Girdi olarak bir sayı alır
    Bu sayıya kadar olan asal sayıları ekrana basar.
    """
    asallar = [2]
    if kaca_kadar < 2:
        return None
    elif kaca_kadar == 2:
        return asallar
    else:
        for i in xrange(3,kaca_kadar,2):
            bolundu = False
            for j in asallar:
                
                if i % j == 0:
                    bolundu=True
                    break
                if j > (i / 2):
                    break
            if bolundu == False:
                asallar.append(i)
    return asallar
    
if __name__ == "__main__":
    # join kullanmak için, listedekiler str olmalı.
    print "\n".join(map(str,asal(20000)))
    """
    Bu aşamada map() bilinmiyorsa, list comprehension veya for döngüsü kullanılır.
    print "\n".join([str(asal) for asal in asal(1000)])
    for asal in asal(1000)
        print asal
    """
    

Kareköke kadar bölme

Bir sayının asal bölenleri, o sayının karekökünden büyük olamaz.

def asal(kaca_kadar):
    """Asal sayı bulan fonksiyon
    Girdi olarak bir sayı alır
    Bu sayıya kadar olan asal sayıları ekrana basar.
    """
    asallar = [2]
    if kaca_kadar < 2:
        return None
    elif kaca_kadar == 2:
        return asallar
    else:
        for i in xrange(3,kaca_kadar,2):
            bolundu = False
            # karekökün aşağı yuvarlanma ihtimaline karşı, + 1 ekliyoruz.
            limit = (i ** 0.5) + 1
            for j in asallar:
                
                if i % j == 0:
                    bolundu=True
                    break
                if j > limit:
                    break
            if bolundu == False:
                asallar.append(i)
    return asallar

Eratosthenes'in Elemesi

Bu yöntemde, 3'den verilen sayıya kadar olan tek sayılar bir listede toplanır. Daha sonra, 3'er 3'er atlanarak, üzerine gelinen sayılar 0'a eşitlenir. Böylece, 3'ün katları elenmiş olur. Daha sonra, ilk 0 olmayan sayı kadar atlanarak üzerine gelinen sayılar 0'a eşitlenir. Böylece, o sayının da katları elenmiş olur. Bu şekilde, en büyük sayının kareköküne kadar devam edilir. Biraz önce bahsettiğimiz gibi, bir sayının kendi karekökünden büyük asal böleni olamaz. Ve, bu sayının karekökü, listedeki bütün sayıların karekökünden büyüktür. Dolayısıyla, bu sayının kareköküne ulaşıldığında, elemeye son verilebilir.

# -*- coding:utf-8 -*-

def asal(kaca_kadar):
    """Asal sayı bulan fonksiyon
    Girdi olarak bir sayı alır
    Bu sayıya kadar olan asal sayıları ekrana basar.
    """
    
    if kaca_kadar < 2:
        return None
    elif kaca_kadar == 2:
        return [2]
    else:
        # 3,5,7...,kaca_kadar
        sayilar = range(3,kaca_kadar + 1, 2)
        kok =  kaca_kadar ** 0.5
        
        for i in xrange(0,len(sayilar)):
            sayi = sayilar[i]
            if sayi > kok:
                break
            if sayi:
                ilk_sil_index = i + sayi
                
                for j in xrange(ilk_sil_index,len(sayilar),sayi):
                    sayilar[j] = 0
            
            
            
    return [2] + [sayi for sayi in sayilar if sayi]
    
if __name__ == "__main__":
    # join kullanmak için, listedekiler str olmalı.
    print "\n".join(map(str,asal(500)))
    """
    Bu aşamada map() bilinmiyorsa, list comprehension veya for döngüsü kullanılır.
    print "\n".join([str(asal) for asal in asal(1000)])
    for asal in asal(1000)
        print asal
    """

Python ve Google Api ile Url Kısaltma

23 December 2011 Yaşar Arabacı
Url kısaltmak için google kullanmak bana kolay geliyor. Çünkü, temel işlemler için, api anahtarına ihtiyaç yok. Tek yapmanız gereken, kısaltmak istediğiniz url'i json formatında, post metoduyla https://www.googleapis.com/urlshortener/v1/url adresine göndermeniz. Python ile bunu nasıl yapılacağına ait aşağıda bir örnek var. İhtiyaca göre düzenlenip, kullanılabilir.

İlk iş, urllib2 ve json modüllerini içe aktarmak. Bu araçlar olmazsa, http isteği göndermek ve aldığımız cevabı kullanabileceğimiz bir python değişkenine dönüştürmek, oldukça güç olur.

from urllib2 import urlopen, Request
from json import loads, dumps

Request ile, bir http isteği hazırlanır. Bu hazırlanan istek, urlopen ile gerçekleştirilir. loads, bir yazıdan, python sözlüğü oluşturur. dumps ise bunun tam tersidir. Öylese, dumps'ın kullanacağı bir sözlük hazırlamak gerekir.

json_sozluk = {"longUrl" : "http://yasar.serveblog.net/post/python-ve-google-api-ile-url-kisaltma/"}

Bu hazırlanan sözlükte, longUrl olmalıdır. Google bizden, bunu ister. Artık http isteği oluşturmak için, gerekli şeyler hazır.

istek = Request(
    "https://www.googleapis.com/urlshortener/v1/url",
    data=dumps(json_sozluk),
    headers={"Content-Type" : "application/json"}
)

Content-type http başlığını "application/json" yapmak şarttır, aksi halde google, isteğinizi reddeder. Artık geriye kalan, Google'dan gelen cevabı okumaktır, ve bundan bir sözlük yapmaktır.

soket = urlopen(istek)

cevap = loads(soket.read())

soket.close()

Son olarak elimizde kalan, şöyle bir python sözlüğü olur. cevap = { "kind": "urlshortener#url", "id": "http://goo.gl/n1G9N", "longUrl": "http://yasar.serveblog.net/post/python-ve-google-api-ile-url-kisaltma/" }. Bu sözlükteki id anahtarıyla belirtilen url, sizin google'a verdiğiniz url'e yönlendirilir. Ancak, google bu url'in zararlı olduğunu düşünüyorsa başka. O zaman, bu siteye yönlendirme yapılmayabilir. Google kullanıcılarını korumaya çalışır.

Url kısaltınca, bunun hakkında bilgi de edinilebilir. Bu basit http isteğiyle olur. İsterseniz yüzeysel yada detaylı bilgiye erişebilirsiniz.

Python'da Hız Ölçme: profile Modülü

23 December 2011 Yaşar Arabacı
Python'da hız ölçmek için profile veya cprofile modülleri kullanılabilir. Bu modüllerin arasındaki fark, ilkinin saf python ile diğerinin ise C ile uygulanmış olmasıdır. Performans açısından, cprofile modülü daha verimlidir. profile modülü, cprofile modülüne göre yavaş kalır, ancak, saf python ile yazıldığından dolayı, genişletmeye daha müsaittir. Bir de, bazı platformlarda, cprofile modülü bulunmayabilir.

profile.run('foo()')

Bu modülün en basit kullanım şekli, bu modüldeki run() fonksiyonunu kullanmaktır. Bu fonksiyon, argüman içerisinde verdiğiniz ifadeyi çalıştırır ve ekrana bazı bilgiler yazar.

import profile
def foo(bar):
    return bar * 2
profile.run('foo(40)')

Bunun çıktısı da şuna benzer:

              4 function calls in 0.000 CPU seconds
    Ordered by: standart name

    ncalls  tottime  percall  cumtime  percall  filename:lineno(function)
           1   0.000    0.000      0.000   0.000  :0(setprofile)
# böyle devam ediyor.

En üst satırda, toplam kaç fonksiyon çağırıldığı ve bunların kaç CPU saniyesi sürdüğünü gösteriyor. Altta, gösterimin sıralamasıyla ilgili bir bilgi var, daha sonra, öğrenmek istediğimiz bilgiler bir tablo halinde sunulmuş. Her satırda, bir fonksiyon için bilgiler sunuluyor. İlk sütun o fonksiyonun toplamda kaç kere çağırıldığını gösteriyor. İkinci sütün ise, o fonksiyon içerisinde, toplamda ne kadar vakit harcandığını gösteriyor. Ancak buna, bu fonksiyon içinde çağırılan diğer fonksiyonların harcadığı zaman dahil değil. Bir başka deyişle, sadece bu fonksiyon içinde yapılan işlemlerin harcadığı zaman bu hesaba dahil. 3. sütundaki percall, tottime/ncalls değerini veriyor. Daha sonraki iki satırda ise, alt fonksiyonların harcadığı zamanı da hesaba katarak, 2. ve 3. satırda yapılan işlemleri tekrarlıyor. Son satırda ise, bu bilgilerin hangi fonksiyona ait olduğu, dosyaadı:satır(fonksiyon) şeklinde gösteriliyor.

profile.run('foo()','bar.txt') ve pstats

run() ile hesaplanan bilgileri bir dosyaya kaydedip yazılımınızı geliştirdikçe referans olarak kullanmak isteyebilirsiniz. Bunu, run("foo()","bar") şeklinde, run() fonksiyonuna ikinci argüman olarak bir dosya adı vererek yapabilirsiniz. Böylece, ölçüm bilgileri, python'un anlayacağı bir dilde, bar isimli dosyaya kaydolur, daha sonra, bu bilgileri okumak için pstats modülü kullanılır.

import pstats
istatistik = pstats.Stats("bar") # argüman olarak, bilgilerin tutulduğu dosyanın yolu.

# Stats sınıfının bazı metotları:

istatistik.strip_dirs().sort_stats(-1).print_stats()
# modül isimlerinden fazlalık olan dosya yollarını siler, son sütuna göre sıralar, bilgileri ekrana yazar.

istatistik.sort_stats('cumulative').print_stats(10)
# en çok vakit harcayan fonksiyona göre sıralar, tepeden 10 tane listeler.

istatistik.sort_stats('time').print_stats(5)
# yukarıdaki gibi, ancak, tottime sütununa göre sıralıyor.

istatistik.sort_stats('dosyaadı').print_stats('hebele')
# dosya adına göre sıralama yapar, ve sadece içinde hebele geçen satırları listeler.

istatistik.print_callers('foo')
# bu fonksiyonu çağıranları listeler. Yukarıdaki print_stats örneklerinin hepsi print_callers ile de yapılabilir.

Burada yazılan bilgiler, muhtemelen python'da programlarınızın profilini çıkarmaya başlamak için yeterli olacaktır. Zaten benim bildiğim de bu kadar. Daha fazlasını öğrenmek isteyenler, Python Profil Modülü Belgeleri'ne bakabilir. Kolay Gelsin.

Python Hakkında Birkaç Soruya Cevap

23 December 2011 Yaşar Arabacı

Bir arkadaşımın Python hakkındaki birkaç sorusu üzerine, bu soruları blog'umdan cevaplamayı ve aynı soruların cevaplarını arayan diğer arkadaşlarla da paylaşmış olmayı istedim.

Sorulara ve cevaplara geçmeden önce, şunu belirtmek istiyorum, python öğrenmeye başlamış eski bir C veya Java geliştiricisiyseniz, bu geçmişiniz Python öğrenmeniz açısından talihsiz bir durum. Python öğrenmeden önce, C, Java, Php gibi diğer dilleri kullanmış geliştiriciler, bu dillerdeki alışkanlıklarını Python'a taşımaya meyillidir. Ancak, Python'un iş yapış şekli, kendine özgüdür. Bu yüzden, Python öğrenmeye başlarken, daha önce programlama ile öğrendiğiniz herşeyi gözardı etmeye çalışın, ve Python felsefesini öğrenmeye çalışın. Bu konuda anlaştıysak, soru-cevap bölümüne geçebiliriz.

Tkinter'de Frame.__init__ gibi bir ibare var. Buradaki mantık nedir?

Açıkcası ben Tkinter bilmiyorum, hiç kullanmadım, ama genel olarak iki alt tireli değişkenler ve sınıf metotlarından bahsetmek isterim. Python'la yeni tanışanlar için, iki alt tireli değişkenler ve metot isimlerinin kafa karıştırıcı olabileceğini, birkaç farklı şekilde gördüm. Eğer bunlar sizin de kafanızı karıştırıyorsa, bunlar hakkında benimsemeniz gereken ilk şey, bunların diğer sınıf metotlarından veya değişkenler bir farkı olmadığıdır.

2 Alt Tireli Değişkenlerin Özellikleri

Python'da ismi iki alt tireli değişkenlerin çoğu, Python tarafından tanımlanan değişkenlerdir, ancak isterseniz, siz de ismi __benimdegisken__ gibi bir değişken tanımlayabilirsiniz. Bu öntanımlı değişkenler, Python'un iç dinamikleri açısından önemlidir. İsimlerinin bu şekilde olmasının nedeni de budur. Böylece geliştiricilerin, farkında olmadan önemli değişkenlerin üzerine yazmasının önüne geçilmiş olur. Bu da demek oluyor ki, bu değişkenleri çalışma anında değiştirebilirsiniz. Ancak, yapmayın, kendi akıl sağlığınız için... Bunlardan sık kullanılan birkaçına örnek vermek istiyorum:

__name__: Bu değişken, bir modülün veya sınıfın çalışma anındaki adını tutar. Çalışma anında ilk çalıştırılan modülün adı "__main__" olur. Gerek doğrudan, gerekse dolaylı olarak içe aktarılan diğer modülüllerin adı ise, paket_adı.modül_adı gibi noktalı gösterim olur. Sınıfların adı ise, nasıl tanımladıysanız, o şekildedir.

"""
bisiler.py
Eğer bu modül, doğrudan çalıştırıldıysa, bisiler_yap fonksiyonunu çalıştırıyor.
Eğer import ile içe aktarıldıysa, hiçbirşey yapılmıyor.
"""
class a:
    def benim_adim(self):
        return self.__class__.__name__
"""
>>> b = a()
>>>b.benim_adim()
'a'
>>>b.__name__
AttributeError: a instance has no attribute __name__
>>> Sınıf instance'ı (tr:örnek) için __name__ tanımlı değildir.
"""
if __name__ == "__main__":
    bisiler_yap()

__file__: Bu değişken, bir modülün dosya yolunu tutar.

"""
os_nerde.py
Bu modül, doğrudan çalıştırıldığında, ekrana os modülünün dosya yolunu
basar.
"""
import os
if __name__ == "__main__":
    print(os.__file__)

__package__: Bu değişken, bir modülün hangi pakete ait olduğunun bilgisini tutar. Eğer modül hiçbir paketin içerisinde değilse, None döndürür.

"""
hangi_paket.py
"""
import os
from django.db import models
print(os.__package__)
# None
print(models.__package__)
# django.db.models

__doc__: Bu değişken, bir modülün belgelendirmesini döndürür. Modül içerisinde iki tırnak arasında (tek satırlık belgelendirme) veya 2 tane 3 tırnak arasında (çok satırlı belgelendirme) arasında kalan yazılar, o modülün belgelendirmesini oluşturur. __doc__değişkeni aynı zamanda, fonksiyonlar, sınıflar ve metotlar için de tanımlıdır.

"""
os_belgeleri.py
"""
import os
print(os.__doc__)
#OS routines for Mac, NT, or Posix depending on what system we're on.
#
#This exports:
#  - all functions from posix, nt, os2, or ce, e.g. unlink, stat, etc.
#  - os.path is one of the modules posixpath, or ntpath
#  - os.name is 'posix', 'nt', 'os2', 'ce' or 'riscos'
#  - os.curdir is a string representing the current directory ('.' or ':')
#  - os.pardir is a string representing the parent directory ('..' or '::')
#  - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\')
#  - os.extsep is the extension separator ('.' or '/')
#  - os.altsep is the alternate pathname separator (None or '/')
#  - os.pathsep is the component separator used in $PATH etc
#  - os.linesep is the line separator in text files ('\r' or '\n' or '\r\n')
#  - os.defpath is the default search path for executables
#  - os.devnull is the file path of the null device ('/dev/null', etc.)
#
#Programs that import and use 'os' stand a better chance of being
#portable between different platforms.  Of course, they must then
#only use functions that are defined by all platforms (e.g., unlink
#and opendir), and leave all pathname manipulation to os.path
#(e.g., split and join).
#

Aklıma gelen özel değişkenler bunlar. Bunlardan başka var mıydı emin değilim.

Özel sınıf metotları

Aynı yukarıda bahsettiğimiz değişkenler gibi, Python için özel anlamı olan sınıf metotları, iki alt tireli olarak isimlendirilir. Bunlardan bazıları şunlardır:

__new__(): Bu metot, yeni bir sınıf örneği (eng:instance) oluşturulacağı zaman çağırılır. Görevi yeni bir sınıf örneği döndürmektir. Bu metoda python manyağı değilseniz hiçbir zaman ihtiyaç duymazsınız. Şahsen ben hiç kullanmadım. İngilizce bilenler, daha detaylı bilgi için: "MetaClass", "__new__", "cls" and "super" - can someone explain the mechanism exactly adresinde yazılanlara bakabilirler.

__call__(): Bu metot sınıfı çağırmak için kullanılır. b = a() gibi bir kodla, sınıfı çağırmış olursunuz. yani, b = a() python tarafından b = a.__call__() şekline çevirilir. Eğer bu metodun üzerine yazmazsanız, bu metot, __new__() metoduyla yeni bir sınıf örneği oluşturur, daha sonra bu örneği döndürür.

__init__(): __new__() metodu tarafından, yeni bir sınıf örneği oluşturulduktan hemen sonra çağırılır. Çok sık kullanılan bir sınıf metodudur. Sınıf örneğinin kullanıma hazır olması için gereken tüm işlemler __init__() metodu içerisinde yapılır.

class Foo:
    def __init__(self):
        "yeni bir sınıf örneği oluşturulduğunda, bu örneğin _sayi özelliği 10 olarak atanır."
        self._sayi = 10

class Bar:
    def __init__(self,sayi=10):
        """
        bu sınıfı oluşturuken, sayıyı değiştirmenize izin verir. Öntanımlı değeri 10dur
        a = Bar()
        b = Bar(20)
        a._sayi => 10
        b._sayi => 20
        """
        self._sayi = sayi

class keyword_args:
    def __init__(self,**kwargs):
        "Oluşturulduğu anda aldığı kelime argümanları kendi özelliği olarak ayarlar."
        for anahtar, deger in kwargs:
            setattr(self,anahtar,deger)

a = keyword_args(hebele = 2,hubele = "yazi", hubelop = [1,2,4])
print(a.hebele)
# 2 çıktısı basar.

__getitem__() ve __setitem__():köşeli parantez gösterimiyle anahtar alma ve anahtar ayarlama işlemlerinde çağırılır. b = a[5] gibi bir ifade kullandığınızda, Python bunu b = a.__getitem__(5) şeklinde çalıştırır.

class Foo:
    def __init__(self):
        self._sozluk = dict()
    def __setitem__(self,anahtar,deger):
        self._sozluk[anahtar] = deger
    def __getitem__(self,anahtar):
        return self._sozluk[anahtar]

a = Foo()
a[25] = "yirmibes" # Aslında burada a.__setitem__(25,"yirmibes") çalıştırılıyor
b = a[25] # burada da a.__getitem__(25) çalıştırılıyor.

__add__() ve __mul__():Bunlar, iki obje toplanırken veya çarpılırken kullanılır. Örneğin a + b yazdığınızda, aslında çalışan şey a.__add__(b) fonksiyon çağrısıdır.

__eq__(), __ne__(), __gt__(), __ge__(): Bunlar iki obje karşılaştırılırken kullanılır. Çoğu zaman True ya da False döndürürler. Örneğin, if a == b python tarafından if a.__eq__(b) şeklinde yorumlanır.

from random import randint
class BasariSansi:
    def __eq__(self,oran):
        if randint(0,oran-1) == 0:
            return True
        else:
            return False
    def __ne__(self,oran):
        return not self.__eq__(oran)
a = BasariSansi()
for i in range(0,100)
if a == 3:
    print("1/3 şansın yaver gitti!")
else:
    print("Şansın yaver gitmedi!")

Sonuç olarak

Python'un en büyük zenginliklerinden birisi de, bize birçok konuda özgürlük sağlıyor olmasıdır. Burada bahsettiğim özel sınıf metotları, tüm sınıf metotlarının çok az bir kısmını içine alıyor. Eğer merak edip diğerlerine de bakmak isterseniz, Python'da veri modelleri ile ilgili belgedeki özel metot isimleri ile alakalı bölüme göz atabilirsiniz.

Python ile Java'daki gibi mouse listener, veya key listener nasıl yapılır?

Soruyu soran arkadaş, hangi uygulamada klavye ve mouse olaylarını takip etmek istediğini söylememiş, o yüzden birkaç farklı uygulamada bakalım.

Tkinter de fare ve klavye olaylarına geriçağırım fonksiyonu

Ben aslında Tkinter bilmiyorum, ama, biraz araştırdım ve Events and Bindings makalesini buldum. Bu makalede anlatılana göre, Tkinter objelerine bind metoduyla geriçağırım fonksiyonu eklenebiliyor. İlk argüman, olay türünü, ikinci argüman ise, çağırılacak fonksiyonu gösteriyor.

# Kaynak: http://www.pythonware.com/library/tkinter/introduction/events-and-bindings.htm
from Tkinter import *

uygulama = Tk()

def mouseOlayi(olay):
    print("%s x %s noktasına tıklandı" % (olay.x,olay.y))

frame = Frame(uygulama, width=100, height=100)
frame.bind("<Button-1>", MouseOlayi)
frame.pack()

uygulama.mainloop()

Bunun detaylarına yukarıda verdiğim linkden bakabilirsiniz.

PyQt Olayları

PyQt'de farklı olaylarla uğraşmanın birkaç farklı yolu. Bunlardan bir tanesi şöyle birşey:

from PyQt4.QtCore import * 
from PyQt4.QtGui import *

class BenimYaziAlani(QLineEdit):
    def event(self,event):
        if event.type() == QEvent.KeyPress:
            self.emit(SIGNAL("TusaBasildi"),event.key())
class Uygulama(QMainWindow):
    def __init__(self,*args,**kwargs):
        QMainWindow.__init__(self,*args,**kwargs)
        self.yazi_alani = BenimYaziAlani()
        self.connect(self.yazi_alani,SIGNAL("TusaBasildi"),self.basilan_tus)
    def basilan_tus(self,tus):
        print("%s tusuna basildi" % tus)

Python'da raise ne işe yarar

Python'da raise, bir hata yükseltmek için kullanılır. Daha sonra istenirse, bu hata try..except bloğuyla yakalanabilir.

class CokAcayipHata(Exception):
    pass

def basitToplama(ilk_sayi,ikinci_sayi):
    "İki sayıyı toplar, argüman olarak sayı verilmediyse hata verir!"
    if issubclass(ilk_sayi,int) and issubclass(ikinci_sayi,int):
        return ilk_sayi + ikinci_sayi
    else:
        raise CokAcayipHata

try:
    sonuc = basitToplama("osman",7)
except CokAcayipHata:
    print("Çok acayip bir hata oluştu, öyle böyle değil!")

Python'da map() ve filter() fonksiyonları ne işe yarar?

map()

map() fonksiyonu, ilk argümanı olarak bir fonksiyon, ikinci argümanı olarak sırayla elemanlarını alabileceği bir nesne alır. Bu nesnenin her elemanını fonksiyona sokar ve sonucunu bir liste olarak döndürür.

def mumdur(kac):
    return str(kac) + " mumdur"
a = map(mumdur,[1,2,5,15])

#a = ["1 mumdur","2 mumdur","5 mumdur","15 mumdur"]
"""
map(mumdur,[1,2,5,15]) aşağıdaki şeyin kısa yoludur:
"""
bos_liste = []
for i in [1,2,5,15]:
    bos_liste.append(mumdur(i))

filter()

filter() fonksiyonu, ilk argümanı olarak bir fonksiyon, ikinci argümanı olarak sırayla elemanlarını alabileceği bir nesne alır. Bu nesnenin her elemanını fonksiyona sokar ve fonksiyonun sonucunun True olduğu elemanları bir liste olarak döndürür.

def cift_sayi(kac):
    return kac % 2 == 0 and True or False
a = fillter(cift_sayi,xrange(0,10))

#a = [0,2,4,6,8]
"""
filter(cift_sayi,xrange(0,10)) aşağıdaki şeyin kısa yoludur:
"""
bos_liste = []
for i in xrange(0,10):
    if cift_sayi(i):
        bos_liste.append(i)

global kelimesi ne işe yarar

Python'da fonksiyonlar gibi kod blokları içerisinde tanımlı olan değişkenler, yerel değişken olur. Buralarda tanımlı olan değişkenler, bu kod bloklarının dışında tanımlı değildir. Böyle kod bloklarının içersinde tanımladığınız değişkenin tüm modül içerisinde kullanılabilir olması için, global kullanılır.


x = 0
def yerel(asdf):
    x = asdf
def genel(asdf):
    global x
    x = asdf

yerel(10)
print(x) # 0 yazar, fonksiyon içindeki x'in dışardaki x'e bir etkisi yoktur.
genel(15)
print(x) # 15 yazar, fonksiyonun içinde, genel x değiştirilmiştir.

Python'da "and" ve "or"

23 December 2011 Yaşar Arabacı
Python'daki and ve or dil yapısının biraz akılda kalması güç bir özelliği var. Bu yüzden buraya biraz özet geçmek istedim. Daha sonra kendim ve başkaları referans olarak bakabilelim diye.

and

and dil yapısı, yanlış ile karşılaştığında, o öğeyi direk döndürür. Eğer yanlış öğe bulamazsa, son öğeyi döndürür. Yanlış olarak değerlendirilebilecek öğeler, boş str, list, dict nesneleri, 0, False veya None olabilir. Bunların dışında neredeyse herşey doğru olarak değerlendirilir.

"K = 'b' olur, tüm öğeler doğru olduğu için, son öge döndürülür."
K = "a" and "b"

"K = 0 olur. İlk yanlış öge olduğu için 0 döner."
K = 0 and "b"

" K = 0 olur, İki yanlış öge olsa da, ilk yanlış öğe olan 0 döndü."
K = "a" and 0 and "" and "c"

def x():
    print "x fonksiyonu"
    return True

"0 döner, x() çalışmaz. İlk öge yanlış olduğu için, ikinci öge degerlendirilmeye çalışılmaz."
0 and x()

or

and yapısının tersine or yapısı ilk doğru ögeyi döndürmeye çalışır.

"K = [] olur, tüm öğeler yanlış olduğu için, son öğe döner."
K = 0 or []

"K = 'b' olur. 'b' ilk doğru öğedir."
K = 'b' or 0

" K = 'a' olur, 'a' ilk doğru öğedir."
K = None or {} or False or "a" or "c"

def x():
    print "x fonksiyonu"
    return True

"0 yanlış olarak değerlendirildiği için, x() fonksiyonu çalışır ve değeri döndürülür."
0 or x()

Zincirleme

and ve or zincirlendiğinde soldan sağa doğru, sırasıyla değerlendirme yapılır.

" 'yasar' döner. 1 and 'yasar', 'yasar' olarak değerlendirilir. 
'yasar' or 'b', 'yasar' olarak değerlendirilir. 
"
1 and "yasar" or "b"

"
'osman' döner.
0 and 'yasar', 0 olarak değerlendirilir. Dönen 0, 'osman' ile karşılaştırılır.
0 or 'osman', 'osman' olarak değerlendirilir ve bu değer döner. 
"
0 and "yasar" or "osman"
"
1 döner. 
1 or 'yasar', 1 olarak değerlendirilir. Dönen 1 ile 'osman' karşılaştırılır.
1 and 'osman', 1 olarak değerlendirilir.
"
1 or "yasar" and "osman"

"
'osman' döner.
0 or 'yasar' => 'yasar'
'yasar' and 'osman' => 'osman'
"
0 or "yasar" and "osman"

If Yapısıyla and-or kullanımı

and ve or, if yapısıyla birlikte kullanıldığında, bunlardan dönen değer, if ile değerlendirilir.

# If içerisine girmez. '' and "b" den '' döner, if bunu yanlış olarak değerlendirir.
if '' and "b"

# If içerisine girer. "" or "b" den "b" döner. if "b"yi doğru olarak değerlendirir.
if "" or "b"

# If içerisine girmez. 0 or None içinde doğru öğe olmadığı için, son öğe olan None döner.
if 0 or None

# If içerisine girmez. 0 or None, 0 döndürür. 0 yanlış olarak değerlendirilir.
if 0 and None

Python'da str Objesini Genişletmek

23 December 2011 Yaşar Arabacı
Python'da str ve unicode gibi objelerin alt sınıflarını oluşturmak istediğinizde, biraz sıkıntıya düşebilirsiniz. Bunlar, yerinde değiştirilemeyen nesneler olduğu için, bunların metotlarını kullanmak, kendi objenizin kaybolmasına neden olur. Bu problemin önüne geçmek için, __getattribute__() metodunun üzerine yazabilirsiniz. Aşağıdaki örnekte, çoğul eki, iyelik eki ve ismin hallerini bulan bununla birlikte, bir unicode objesinin de tüm metotlarına erişebilen bir sınıf tanımlayacağız.
# -*- coding:utf-8 -*-
kalin = (u"a",u"ı",u"o",u"u")
ince = (u"e",u"i",u"ö",u"ü")

yumusayan = {
    u"k": u"ğ",
    u"t": u"d",
    u"p": u"b"
}

sertlestirenler = (u"f",u"s",u"t",u"k",u"ç",u"ş",u"h",u"p")

class kelime(unicode):

    def __getattribute__(self, isim):
        "unicodedan gerekli metodu alır, sonucu kelime olarak döndürür."
        att = super(kelime, self).__getattribute__(isim)

        if not callable(att):
            return att

        def sonra_cagir(*args, **kwargs):
            sonuc = att(*args, **kwargs)
            if isinstance(sonuc, unicode):
                return kelime(sonuc)
            return sonuc
        return sonra_cagir
        
    def __add__(self,other):
        "iki kelimeyi unicode toplamıyla toplar, sonucu kelime olarak döndürür"
        return kelime(unicode.__add__(self,other))
        
    def __mul__(self,other):
        "bir kelimeyi bir sayıyla unicode olarak çarpar. Sonucu kelime olarak döndürür"
        return kelime(unicode.__mul__(self,other))
    
    @property
    def lar(self):
        "Kelimeye çoğuk eki ekler"
        for harf in reversed(self):
            if harf in kalin:
                return kelime(self + u"lar")
            elif harf in ince:
                return kelime(self + u"ler")
                
        return kelime(self + u"lar")
    
    @property
    def in_(self):
        "kelimeye iyelik eki ekler"
        _kelime = self
        if _kelime[-1] in yumusayan:
            _kelime = _kelime[:-1] + yumusayan[_kelime[-1]]
        elif _kelime[-1] in kalin:
            return kelime(_kelime + u"nın")
        elif _kelime[-1] in ince:
            return kelime(_kelime + u"nin")
        
        for harf in reversed(_kelime):
            if harf in kalin:
                return kelime(_kelime + u"ın")
            elif harf in ince:
                return kelime(_kelime + u"in")
        
        return kelime(_kelime + u"ın")
    @property   
    def e(self):
        "kelimeye ismin e halini ekler"
        _kelime = self
        if _kelime[-1] in yumusayan:
            _kelime = _kelime[:-1] + yumusayan[_kelime[-1]]
        elif _kelime[-1] in kalin:
            return kelime(_kelime + u"ya")
        elif _kelime[-1] in ince:
            return kelime(_kelime + u"ye")
        
        for harf in reversed(_kelime):
            if harf in kalin:
                return kelime(_kelime + u"a")
            elif harf in ince:
                return kelime(_kelime + u"e")
        
        return kelime(_kelime + u"a")
    
    @property
    def i(self):
        "kelimeye ismin i halini ekler"
        _kelime = self
        
        if _kelime[-1] in yumusayan:
            _kelime = _kelime[:-1] + yumusayan[_kelime[-1]]
        elif _kelime[-1] in kalin:
            return kelime(_kelime + u"yı")
        elif _kelime[-1] in ince:
            return kelime(_kelime + u"yi")
        
        for harf in reversed(_kelime):
            if harf in kalin:
                return kelime(_kelime + u"ı")
            elif harf in ince:
                return kelime(_kelime + u"i")
        
        return kelime(_kelime + u"ı")
    @property   
    def de(self):
        "kelimeye ismin de halini ekler."
        if self[-1] in sertlestirenler:
            ek = u"t"
        else:
            ek = u"d"
        
        for harf in reversed(self):
            if harf in kalin:
                return kelime(self + ek + u"a")
            elif harf in ince:
                return kelime(self + ek + u"e")
        
        return kelime(self + u"da")
    @property   
    def den(self):
        "kelime ismin den halini ekler"
        return kelime(self.de + u"n")

Yukarıdaki örneği şu şekilde çalıştırabilirsiniz.

a = kelime(u"yaşar")
"""
Normalde unicode'a ait metotları kullanmak, farklı bir unicode döndürmesi
gerekir. Ama __getattribute__ metodunun üzerine yazarak, bunun üstesinden geldik.
"""
type(a.lower()) # <class 'kelime'>
"""
Her defasından kendi sınıfımız döndürüldüğü için, unicode sınıfının ve kendi sınıfımızın metotlarını
istediğimiz gibi zincirleyebiliriz.
"""
print(a.capitalize().lar.de) # "Yaşarlarda"
type(a.lar.capitalize().de) # <class 'kelime'>
print(a + " hebele") # "yaşar hebele"
type(a + hebele) # <class 'kelime'>

Bu sistemin bütün mahareti, __getattribute__() içinde tanımladığımız sonra_cagir fonksiyonu. Örneğin, kelime.lower() çağırıldığında, aslında çalışan fonksiyon şöyle oluyor.

sonuc = unicode.lower(self)
if issubclass(sonuc,basestring):
    # sonuc bir unicode idi, döndürmeden önce bunu tekrar kelime yapıyoruz.
    return kelime(sonuc)

# unicode objesinin bazı metotları, yazı harici şeyler döndürebilir. Bu durumlarda
# ne aldıysak onu döndürüyoruz.
return sonuc

Python ile Çeşitli Yazılım Geliştirme Desenleri (Design Patterns)

23 December 2011 Yaşar Arabacı

İnsan programlamayı kendi çabalarıyla öğrenince, işin okulunu okumuş insanlardan bazı konularda eksik kalıyor. En azından ben böyle hissediyorum. Hem aradaki açığı kapatmak, hem de kendimi geliştirmek adına, birkaç yazılım geliştirme deseni öğrenmek ve bunları Python ile uygulamaya geçirmek istedim. Bunu yapmanın hem kendi gelişimime faydası olacağını düşünüyorum, hem de bunu okuyan birkaç kişinin, en azından konuya giriş yapmış olması açısından, işini kolaylaştıracağını zannediyorum.

Singleton

Bu desende, bir sınıfın sadece tek bir sınıf örneği (class instance) oluşturmasını sağlayacak bir mekanizma oluşturuyoruz.Böylece programın tüm parçalarının tek bir obje üzerinden işlem yapmasını sağlayabiliriz.[1] Benim uygulamam şu şekilde oldu.

class singleton(object):
	instance = None
	def __new__(cls,*args,**kwargs):
		if cls.instance is None:
			cls.instance = super(singleton, cls).__new__(cls,*args,**kwargs)
		return cls.instance
	def __init__(self):
		if not hasattr(self,"sayi"):
			self.sayi = 5

"""		
>>> a = singleton()
>>> a.sayi
5
>>> a.sayi += 3
>>> a.sayi
8
>>> b = singleton()
>>> b.sayi
8
>>> 
"""

Burada, instance isminde bir sınıf değişkeni tuttum. Bu sınıf değişkenini None olarak ayarladım. Bildiğiniz gibi, __new__() metoduyla yeni bir sınıf örneği oluşturulmasını kontrol ediyoruz. Burada, eğer instance değişkeni None ise, yeni bir örnek oluşturup, bunu instance değişkenine atıyorum. Daha sonra, her yeni sınıf örneği oluşturulmaya çalışıldığında, daha önce oluşturmuş olduğum örneği döndürüyorum.

__new__() metodu her çalıştığında, eğer bir sınıf örneği döndürüyorsa, __init__() metodu da çalışıyor. Bu yüzden, her seferinde __init__() metodunda yapılan işlerin yinelenmesi, her yeni sınıf çağırılışında, burada tanımlanan değerlerin sıfırlanması anlamına geliyordu. Bu yüzden, örneğe ait yeni bir değişken oluştururken, bunun daha önce var olmadığından emin olmak gerekiyor. Böylece üstüne yazmamış oluyoruz.

Bir google aramasıyla birkaç farklı singleton uygulanışı bulmak mümkün. Benim bulduklarım arasından en ilginç gelen buradaki dekoratör fonksiyon. Ancak yine de, kendi yazdığım şekli bana daha düzgün bir kodlama gibi geliyor. Tabii ki bunlar kişisel görüşlere bakan şeyler.

Sorumluluk Zinciri (Chain Of Responsibility)

Bu yöntem zannımca en çok olay yönetiminde kullanılıyordur. Örneğin arayüz uygulamalarının çeşitli klavye ve fare olaylarını halledişi gibi. Bu desende, bir objeden bir iş istediğinizde, mesela bir tuşa basılma olayının halledilmesi gerektiğinde, eğer bu işi kendi üstlenebiliyorsa, kendi üstleniyor, yoksa, ebeveyn objeden bu işi yapmasını istiyor. Ebeveyn obje de aynı şekilde kendisi yapamıyorsa, bir üst objeye yönlendiriyor ve böylece bir sorumluluk zinciri oluşturulmuş oluyor. Bu deseni uygulamak için, oluşturduğumuz objenin iki yapıyı kurması gerekiyor. Birincisi, hangi işi yapabileceğine karar vermek için gereken bir mantık, ikincisi de ebeveyn objeye ulaşması için gereken bir erişim noktası. Gerçek hayatta kullanılamayacak kadar aptal bir örnek olsa da, şöyle birşey yazdım bununla ilgili:

class StringHandler(object):
	def __init__(self,parent=None):
		self.parent = parent
		self.canHandle = []
	def handle(self,string):
		if string in self.canHandle:
			self._handle(string)
		elif self.parent != None:
			self.parent.handle(string)
		else:
			self.defaultHandler(string)
	def _handle(self,string):
		print("handled by %s" % self)
	def defaultHandler(self,string):
		print("default handled by %s" % self)

		
class osmanHandler(StringHandler):
	def __init__(self,parent=None):
		super(osmanHandler,self).__init__(parent)
		self.canHandle = ["osman"]

	
class orhanHandler(StringHandler):
	def __init__(self,parent=None):
		super(orhanHandler,self).__init__(parent)
		self.canHandle = ["orhan"]

"""		
>>> a = osmanHandler()
>>> a.handle('osman')
handled by <__main__.osmanHandler object at 0x026D6910>
>>> a.handle('orhan')
default handled by <__main__.osmanHandler object at 0x026D6910>
>>> b = orhanHandler(a)
>>> b.handle('orhan')
handled by <__main__.orhanHandler object at 0x026D6810>
>>> b.handle('osman')
handled by <__main__.osmanHandler object at 0x026D6910>
>>> b.handle('cengiz')
default handled by <__main__.osmanHandler object at 0x026D6910>
>>> 
"""

Burada, aslında hiçbir işe yaramayan bir sınıf tanımladım. Bu sınıfın, canHandle listesi, bu sınıfın hangi yazıların işlenmesinden yükümlü olduğunu belirtiyor. Daha sonra, bu sınıftan miras alan iki yeni sınıf daha tanımladım. Bunlardan birinin işi "osman" kelimesini işlemek, diğerinin işi de "orhan" kelimesini işlemek gibi düşünebilirsiniz. Bu sınıfların yeni bir örneğini oluştururken, hangi sınıfın bunun ebeveyni olacağını belirleyebiliyoruz. Eğer belirtmezsek, bu sınıf en tepe sınıfmış gibi kabul edilecek. Herhangi bir sınıfın handle(string) metodu çağırıldığında, kendisi bu yazıyı işleyebiliyorsa işleyecek, işleyemiyorsa, bir üst objeye gönderecek. Tepeye kadar işlenmeden gelirse, ve tepe obje de bunu işleyemezse, öntanımlı metod (defaultHandler) çalışacak.

Ara objeler (Proxy)

Bu desende, asıl objeye, başka bir obje üzerinden ulaşılıyor. Bunun amacı, asıl objeye erişimi kontrol etmek. Asıl objeye erişimi neden kontrol etmek isteyeceğiniz programınızın gereksinimlerine göre değişir. Ben göstermelik olsun diye, şöyle birşey yaptım.

class kibar(object):
	def __init__(self,main):
		self.main = main
	def __getattribute__(self,attribute):
		main = super(kibar,self).__getattribute__("main")
		if attribute == "main":
			return main
		if not attribute.endswith("_lutfen"):
			raise AttributeError("Kibar olun! -> %s_lutfen" % attribute)
		
		return getattr(main,attribute.rstrip("_lutfen"))
	def __getitem__(self,key):
		return self.main[key]
	def __setitem__(self,key,item):
		self.main[key] = item
	def __str__(self):
		return self.main.__str__()

"""	
>>> a = kibar('osman')
>>> a
<__main__.kibar object at 0x025990B0>
>>> str(a)
'osman'
>>> print(a)
osman
>>> b = kibar([1,2,3])
>>> b.append(5)

Traceback (most recent call last):
  File '', line 1, in 
    b.append(5)
  File '', line 9, in __getattribute__
    raise AttributeError('Kibar olun! -> %s_lutfen' % attribute)
AttributeError: Kibar olun! -> append_lutfen
>>> b.append_lutfen(5)
>>> print(b)
[1, 2, 3, 5]
>>> b.extend_lutfen([7,8,9])
>>> print(b)
[1, 2, 3, 5, 7, 8, 9]
>>> b.extend

Traceback (most recent call last):
  File '', line 1, in 
    b.extend
  File '', line 9, in __getattribute__
    raise AttributeError('Kibar olun! -> %s_lutfen' % attribute)
AttributeError: Kibar olun! -> extend_lutfen
"""

Burada kullandığım yöntemin herhalde en can sıkıcı noktası çift alt çizgili bütün metotların tek tek üzerine yazmanın gerekliliği. Çünkü, bu metotları çağırmak için __getattribute__() metodu kullanılmıyor. Bunun üstesinden gelmek için, zannediyorum bir çok akrobasi yapılabilir, ancak, onlara girmeye yeltenmeyeceğim bile.

Düzenleme: Onun için de şöyle birşey düşündüm:


def MakeProxy(obje):
	from collections import Counter
	class proxy(obje):
		def __init__(self,*args,**kwargs):
                        super(proxy,self).__init__(*args,**kwargs)
                        self.counter = Counter()
                def __getattribute__(self,attr):
                        counter = super(proxy,self).__getattribute__("counter")
                        if attr == "counter":
                            return counter
                        counter[attr] += 1
                        return super(proxy,self).__getattribute__(attr)
        return proxy
"""
>>> list_proxy = MakeProxy(list)
>>> a = list_proxy((1,2,3,4))
>>> a
[1, 2, 3, 4]
>>> a.extend([7,8,9])
>>> a
[1, 2, 3, 4, 7, 8, 9]
>>> a.counter['extend']
1
>>> dict_proxy = MakeProxy(dict)
>>> b = dict_proxy({})
>>> b
{}
>>> b['osman'] = 'arabaci'
>>> b
{'osman': 'arabaci'}
>>> b.keys()
['osman']
>>> b.counter['keys']
1

"""

Python ile kayıt (log) dosyaları

23 December 2011 Yaşar Arabacı

Son uğraştığım ufak programda Python'un logging modülünü kullanarak kayıt işlemleri yapıyorum. Kısaca nasıl yapıldığını açıklayayım dedim.

Python ile logging modülü kullanmak için gereken ilk iş, logging modülünü içe aktarıp, getLogger ile yeni bir kayıt tutucu oluşturmak. Bunun nasıl yapıldığını görmek için, aşağıdaki örnek python kodunu inceleyelim.

import logging

# "benim-program-ana-modul" adında yeni bir kayıtçı al
ana_modul_kayitci = logging.getLogger("benim-program-ana-modul")
# bu kayıtçı debug seviyesinde kayıt yapsın.
ana_modul_kayitci.setLevel(logging.DEBUG)

Daha sonra, bu kayıtçı için en az 1 tane handler (bundan sonra işleyici olarak bahsedilecek) eklememiz gerekiyor. Bu işleyicilerin çoğu logging.handlers içersinde bulunurken, aşağıda kullandığım iki tanesi, doğrudan logging modülü içerisinde tanımlanmış. Daha önce belirttiğim gibi sadece bir tane işleyici yetecektir, ben örnek oluşturması için, iki tanesini aşağıdaki python kodundan belirttim.

# logging modulünde tanımlanmış dosyaya kaydedici.
# argüman olarak kayıt dosyasının yolu.
dosya = logging.FileHandler("benim-program-ana-modul.log")

# logging modülünde tanımlanmış standart çıktıya kaydedici
standard_cikti = logging.StreamHandler()

Böylece, elimizde iki adet işleyici var (logging.StreamHandler ve logging.FileHandler). Bu işleyicilerin görevi, kayıt tutulmasını istediğiniz bilgileri gerekli şekillerde kullanmak. Örneğin yukarıdaki örnekte, bir işleyicinin görevi, kayıt altına alınan bilgileri ekrana basmak (logging.StreamHandler), bir diğerinin görevi ise, kayıt altına alınan bilgileri dosyaya kaydetmek (logging.FileHandler). Bunlar gibi daha birkaç çeşit çekirdek python modüllerinde tanımlanmış kayıt işleyiciler var. Şimdi bunlara çıktı formatı ekleyelim. Bunun için logging.Formatter kullanacağız. İkisine ayrı ayrı çıktı formatı eklenebilir, Örnekte tek bir format oluşturup, iki işleyici için de bunu kullanacağım.

# Örn: 2011-09-28 11:45:58,394 - hebele [CRITICAL]: kayıtçı hazır
log_format = logging.Formatter('%(asctime)s - %(name)s [%(levelname)s]: %(message)s')

dosya.setFormatter(log_format)
standart_cikti.setFormatter(log_format)

Böylece kayıt işleyicilerimiz için kayıt formatı belirledik. Bu formatı kişisel zevklerinize ve ihtiyaçlarınıza göre düzenleyebilirsiniz. Şimdi tek ihtiyacımız bu işleyicileri kayıt tutucumuza eklemek, böylece kayıt dosyası oluşturmaya hazır hale geleceğiz.

ana_modul_kayitci.addHandler(dosya)
ana_modul_kayitci.addHandler(standart_cikti)

# artık kayıt tutmaya hazırız.

ana_modul_kayitci.info("kayıt tutucu hazır") # bilgi mesajı
ana_modul_kayitci.warning("Kendine mukayyet ol!") # uyarı mesajı
ana_modul_kayitci.critical("Elektirikler kesildi!") # Kritik bilgi
ana_modul_kayitci.error("Elim kapıya sıkıştı.") # Hata mesajı
ana_modul_kayitci.debug("Nefes aldım.") # hata ayıklama mesajı
ana_modul_kayitci.debug("Nefes verdim.") # hata ayıklama mesajı
ana_modul_kayitci.debug("Tekrar nefes aldım.") # hata ayıklama mesajı

Böylece python'da logging modülünü kullanarak kayıt işlemi yapmış olduk. Kolay Gelsin.

Python Kurcalamaca

23 December 2011 Yaşar Arabacı

Nereden estiyse, Python kaynak kodlarını kurcalamak geldi içimden. Aslında, Python'u Türkçeleştirmek hep içimde bir ukde olmuştur. Bende hazır kodları açıp bakmışken, continue kelimesinin Türkçe karşılığını Python'a ekleyiverdim :)

Türkçe Python

23 December 2011 Yaşar Arabacı

Python'un kaynak kodlarını kurcalamaya devam ediyorum. Basit anahtar kelimelere alternatif eklemek (davranışı değiştirmeden) zannettiğimden daha kolaymış. Sağolsun Nick Coghlan (Python çekirdek geliştiricisi) bunu anlamamdan yardımcı oldu. Ancak, belli yapılarda kelimelerin yerini değiştirmeye çalışınca ayrıştırıcı (parser) patlıyor. Yine Nick Coghlan'ın dediğine göre, Python ayrıştıcısı o kadar zeki değilmiş. Ama ilerleyen günlerde bazı dil yapılarında da kelimelerin yerleşimini değiştirmeyi düşünüyorum. Şu anda geldiğim noktanın özeti aşağıdaki videoda.

Python için Matematik Araçları Kütüphanesi

23 December 2011 Yaşar Arabacı

Beni twitter'dan takip edenler belki hatırlayacaklardır, birkaç gün önce C ile bazı matematik problemlerinin çözümüne yönelik bir Python kütüphanesi yazmaya başlamıştım. O kütüphaneyi bir Python modülüyle biraz daha geliştirdim. Bu kütüphaneyi son aldığı haliyle burada biraz açıklamak ve isteyenlerle paylaşmak istedim. Sanırım şu anda kullanıma hazır.

Nerden Bulabilirsiniz?

Bu paketi kullanmak isteyenler kaynak kodlara Python Paket Indexi'nden ulaşabilir. Ayrıca güncel kaynak kodları github üzerinden takip edebilir, isterseniz katkıda bulunabilirsiniz.

Kurulum

Daha önce de belirttiğim gibi, paketin bir kısmını C ile yazdım, bu yüzden kaynak koddan kurulum yapmak için bilgisayarınızda bir C derleyicisi bulunması gerekiyor. Henüz hiçbir dağıtım için paketlenmiş değil, bu yüzden kaynak koddan kurulum dışında bir kurulum seçeneği yok. Kaynak koddan kurulumu iki şekilde yapabilirsiniz. Birincisi doğrudan pip install mataraclari komutunu vererek kurulum yapmak. Eğer pip komutu bulunamıyorsa easy_install mataraclari komutu da aynı işi görecektir. Diğer bir seçeneğiniz için, yukarıda bahsettiğim kaynaklardan, (tercihen pypi) kaynak kodları indirip, setup betiği ile kurulum yapmak. Bunun için ./setup.py install komutunu vermeniz yeterli, böylece paket derlenip kurulacak. Eğer bazı linux dağıtımlarına paket olarak hazırlayan çıkarsa da güzel olur bence.

Kullanım

Kullanımı uzun uzun yazmaktansa, kısa bir video daha hazırlayıp göstermek istedim. Birkaç gün önce paylaştığım videoda henüz olmayan birkaç fonksiyon daha var videoda.

Python ve Excel Kütüphaneleri

23 December 2011 Yaşar Arabacı

Python ile Excel dosyaları okumak ve yazmak, anladığım kadarıyla merak edilen bir konu ve günlük Python kullanımında ihtiyaç duyulabilecek bir bilgi. Bu yüzden, üstünkörü de olsa, konuya giriş için küçük bir belge yazmak istedim. Python kullanarak Excel dosyalarıyla çalışmak için geliştirilmiş, benim bildiğim, birkaç tane kütüphane var. Bunlardan xlwt Excel'e yazmak, xlrd ise Excel'den okumak için hazırlanmış. Diğer bir kütüphane olan xlutils ise, bunlar arasında bir nevi tutkal görevi görerek, yardımcı fonksiyonlarla bu ikisini birbirine bağlıyor. Bu yazıda, xlrd kütüphanesinden kısaca bahsedip, diğer kütüphanelere de ileriki zamanlarda değinmek istiyorum.

Feragat:Aşağıda yazdıklarım çoğunlukla burada bulabileceğiniz belgenin tembel çevirisidir. Birebir çevirmedim, ama orada olanları aktarıyorum. Bu yazıyı eğer bir şekilde başka bir platformda aynen veya değiştirerek yeniden yayınlayacaksanız, ki yayınlayabilirsiniz, orjinal metine atıfta bulunmanız gerekir.

Kurulum

Yukarıda adı geçen paketlerin hepsine pypi üzerinden ulaşılabilir. Yani eğer sisteminizde setuptools kuruluysa, ki değilse kurmalısınız, easy_install paket_adi komutuyla bunlardan herhangi birini yükleyebilirsiniz. pip ile de aynı şekilde pip install paket_adi komutuyla yükleyebilirsiniz. Kök dizinine yazma hakkı olmayanlar, --user anahtarıyla kurulumu ev dizinlerine yapabilirler (pip veya easy_install farketmez.). Zannediyorum çoğu gnu/linux dağıtımının resmi veya kullanıcı depolarından da bu kütüphanelere ulaşılabilir. İsterseniz bu paketleri teker teker kurabilirsiniz, ama benim tavsiyem xlutils paketinin kurulması, xlutils ile birlikte gerekli xlrd ve xlwt paketleri, ayrıca bazı diğer paketler de kurulmuş oluyor. Böylece, Python ile excel dosyalarıyla çalışmak için gereken tüm kütüphaneleri tek bir komutla yüklemiş oluyorsunuz.

Python ile Excel Dosyaları Okumak

Çalışma Kitabı Açmak

Çalışma kitabı açmak için, xlrd paketi ile gelen open_workbook fonksiyonu kullanılıyor. Bu fonksiyona ilk argüman olarak dosya yolunu verirseniz, o dosyayı okuyup, ilişkili bir xlrd.Book sınıfı döndürüyor. Dolayısıyla bu dosyayla çalışmak için dönüş değerini bir değişkene atamalısınız.

from xlrd import open_workbook
a = open_workbook("birexceldosyasi.xls")

# alternatif olarak:

with open("birexceldosyasi.xls","rb") as dosya:
    # dosyayı binary olarak açtığımıza dikkat ediniz.
    a = open_workbook(file_contents=dosya.read())

# Ayrıca, file_contents olarak mmap objesi de verebilirsiniz.
# bkz: http://docs.python.org/library/mmap.html

Çalışma Kitabında Gezinme

Bir çalışma kitabı açtıktan sonra, muhtemelen bu dosyanın içerisinde gezinip bazı satır ve sütünlardan bilgi almak isteyeceksiniz. Bunun için xlrd.Book objesinin bazı metotlarından yararlanacağız, aşağıdaki örneği inceleyelim.

from xlrd import open_workbook

a = open_workbook("dosyaadi.xls")

# Bir çalışma kitabı, birkaç farklı çalışma sayfadan oluşur. Her sayfada farklı bir tablo vardır.
# bunlara erişmek için xlrd.Book.sheets() metodunu kullanabiliriz.

for sayfa in a.sheets():
    # Bu satırda sayfa değişkenine bir xlrd.sheet objesi atandı.
    
    print "Çalışma sayfası: " + sayfa.name
    
    # sayfadaki satır sayısına nrows ile ulaşıyoruz.
    for satir in range(sayfa.nrows):
        degerler = []
        
        # Sütün sayısına ncols üzerinden ulaşıyoruz
        for sutun in range(sayfa.ncols):
            # cell metodu bize bir hücre döndürüyor, value ile değer okuyoruz.
            values.append(sayfa.cell(satir, sutun).value)
        print "\t".join(degerler)
    print

Çalışma Kitabını İncelemek

open_book() ile dönen xlrd.Book sınıfı bir çalışma kitabıyla ilgili tüm bilgileri tutar. Bu sınıfın nsheets özelliği, çalışma sayfalarının sayısını tutar. Bu sayıyı sheet_by_index metoduyla birlikte kullanmak, çalışma sayfalarını almak için en sık kullanılan yoldur.

sheet_names metodu, çalışma sayfalarının adını döndürür. Nasıl sheet_by_index metoduyla belli bir sıradaki çalışma sayfasını alabiliyorsak, sheet_by_name metodunu kullanarak, belli bir isimdeki çalışma sayfasını alabiliriz. Ayrıca, sheets metodu da tüm çalışma sayfalarının bir listesini döndürür.

a = open_book("birexceldosyasi.xls")

# Index'e göre sayfaları alma
for i in range(a.nsheets):
    print a.sheet_by_index(i)

# Isime göre sayfaları alma
for name in a.sheets_names():
    print a.sheet_by_name(name)

for sayfa in a.sheets():
    print sheet

Çalışma Sayfasını İncelemek

Yukarıda bahsedilen herhangi bir metot ile elde edeceğiniz xlrd.sheet.Sheet objesi, bir çalışma sayfasıyla ilgili tüm bilgileri tutar. Bu objenin name özelliği bu sayfanın adını tutar. ncols ve nrows ise sırasıyla bu sayfadaki sütun ve satır sayısına karşılık gelir.

sayfa = a.sheet_by_index(0)
for satir_no in range(sayfa.nrows):
    for sutun_no in range(sayfa.ncols):
        print sayfa.cell(satir_no, sutun_no).value

Sayfa İçeriğinin Topluca Alınması

Yukarıda üstünkörü bahsettiğim hücre hücre erişim dışında, bir gurup hücredeki bilgileri toplu halde de alabilirsiniz.

# import'lar burada ...
a = open_workbook("bir dosya")
sayfa0 = a.sheet_by_index(0)

# col ve row sırasıyla bir sütun ve satır döndürür. (cell objeleri listesi olarak)

satir0 = sayfa0.row(0)
sutun0 = sayfa0.col(0)

# row_slice ve col_slice ise, belli bir satır ve sütunun bir dilimini döndürür.

# 1. satirin 5. sütundan itibaren olan kısmı
k = sayfa.row_slice(1,5)

# 1. (sayfaya 0'dan başlayarak..) satirin 5 ile 10 sütünları arası
g = sayfa0.row_slice(1,5,10)

# col_slice'de aynı şekilde çalışıyor.

# row_values ve col_values, yukardaki row_slice ve col_slice gibidir.
# ancak, xlrd.sheet.Cell objesi yerine, direk hücrenin değerlerinin
# bir listesini döndürür.

# İlk satirdaki tüm hücrelerin değerleri
birinci_satir_degerleri = sayfa0.row_values(0,0)

Sonuç Olarak

xlrd kütüphanesinin burada bahsetmediğim birkaç özelliği daha var, ama ben detaylara girmeden konuya giriş yapmış olmak istediğimden, sadece temel özelliklere değindim. İleriki zamanlarda,xlwt ve xlutils ile ilgili açıklamaları da blog'uma eklemek istiyorum, ama zaman ne getirecek bilemeyiz.

Python ile soket programlama ve multiprocessing modülü

23 December 2011 Yaşar Arabacı
Python'da çoklu işlem (multiprocessing) ve soket programlama ile ilgili bir örnek. Bugün canım sıkılınca bunlarla uğraştım biraz. Çalışınca bir yere not alayım dedim. Bunlarla uğraşmak isteyenlere de bir başlangıç noktası olmuş olur. Yaptığım şey özetle şu; 5 tane işlem port 9090'ı dinliyor. Gelen isteklere sırasıyla cevap veriyorlar. Python içindeki Threading modülünden farkı ne derseniz, Python'daki GIL (Global Intrepreter Lock) yüzünden, threading modülü ile birden fazla işlemciyi aynı anda kullanamıyorsunuz. Ancak multiprocessing ile birden fazla işlem çalıştığı için, işlemcinizin tüm olanaklarından yararlanabilirsiniz :)
import multiprocessing as mp
import logging
import socket
import time

logger = mp.log_to_stderr(logging.DEBUG)

def worker(socket):
    while True:
        client, address = socket.accept()
        logger.debug("{u} connected".format(u=address))
        client.send("HTTP/1.1 200 OK\r\n")
        client.send("Content-Type: text/html\r\n\r\n")
        client.send("<h1>OK</h1>")
        client.close()
if __name__ == '__main__':
    num_workers = 5

    serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serversocket.bind(('',9090))
    serversocket.listen(5)

    workers = [mp.Process(target=worker, args=(serversocket,)) for i in
            range(num_workers)]

    for p in workers:
        p.daemon = True
        p.start()

    while True:
        try:
            time.sleep(10)
        except:
            break


Ekleme:

Ayrıca, Python'daki SocketServer modülün kullanarak şöyle birşeyler yapmak da mümkün:
import SocketServer
class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024)
        self.request.send(self.data)

if __name__ == "__main__":
    HOST, PORT = "localhost", 9090
    server = SocketServer.ForkingTCPServer((HOST,PORT), MyTCPHandler)
    server.serve_forever()

Burada normal TCP server yerine, Forking TCP server kullanmayı tercih ettiğime dikkat edin. SocketServer.TCPServer sınıfı, her seferinde tek bir istemciye cevap verecek şekilde çalışıyor. ForkingTCPServer ise, her yeni bağlantı için yeni bir işlem oluşturuyor (fork ediyor.). Böylece birden fazla bağlantıya aynı anda cevap verebiliyor. Çalışma şekli benim yukarıda yaptığımdan biraz daha farklı ama, alınan sonuçlar birbirine yakın.

Python Beautiful Soup Kütüphanesi ve Basitçe Kullanımı

23 December 2011 Yaşar Arabacı

Beautiful Soup Python için bir HTML ve XML ayrıştırıcısıdır (parser). Beautiful Soup kütüphanesi kullanışlı olmasını şu özelliklerine borçludur:

  1. Beautiful Soup kötü girdi verseniz bile bozulmaz. Neredeyse orjinal belgenizle aynı anlama gelen bir ayrıştırma ağacı (parse tree) döndürür. Bu özellik çoğu zaman gereken bilgiyi almanız için yeterlidir.
  2. Beautiful Soup bir ayrıştırma ağacında kolayca gezinme (traversing), arama ve düzenleme yapmanıza olanak sağlayan birçok metot ve Python vari deyimler sağlar: her uygulama için baştan HTML veya XML ayrıştırıcı yazmanıza gerek kalmaz.
  3. Beautiful Soup gelen belgeleri Unicode'a, giden belgeleri de UTF-8'e kendiliğinden çevirir. Kodlamalarla uğraşmanıza gerek kalmaz.

Beautiful Soup resmi web sitesi

Beautiful Soup Örnekleri

# -*- coding : utf-8 -*-
from BeautifulSoup import BeautifulSoup
basit_html = """
<html><head><title>Başlık buraya>/title></head>
<body><p>Paragraf 1</p><p class="hebele">Paragraf 2</p></body>
</html>"""
soup = BeautifulSoup(basit_html)

print soup.html.head.title
# ekrana "<title>Başlık buraya</title>" yazar.

print len(soup('p'))
# 2 yazar. Belgedeki p tagı sayısı

print soup('p', {"class": "hebele"})
# [<p class="hebele">Paragraf 2</p>] -> class="hebele" olan tagların listesi.

head = soup.html.head
print head
#<head><title>Başlık buraya</title></head>

head.next
#<title>Başlık buraya</title>
head.next.string
# u'Başlık buraya'

# tag'ın özelliklerine, tag sanki sözlükmüş gibi erişebiliyoruz.
soup.find('p',{"class" : "hebele"})["class"]
# u'hebele'

for i in soup.body:
    print i	

# <p>Paragraf 1</p>
# <p class="hebele">Paragraf 2</p>

Python Beautiful Soup resmi belgeleri burada. Buna benzer bir de şu var.

Örneklerle Python Metasınıflar

23 December 2011 Yaşar Arabacı
Bugün Eli Bendersky'ye ait Python metaclasses by example makalesini okudum. Metasınıflar benim için hep üç aşağı beş yukarı havada kalan kavramlar olmuşlardır. Bahsettiğim makaleyi okuduktan sonra, kafamda bir nebze daha iyi oturtabildim bunları. Bunun üzerine bu makaleyi Türkçe'ye çevirip paylaşmaya karar verdim. Tabi ki, Eli Bendersky'nin izni ile. Aşağıda makalenin tam metninin çevirisini bulabilirsiniz:

Python, çalışma yapısını ve özelliklerini gizleyen birçok "sihir" olmayışından ve diğerlerinden daha açık bir dil olduğundan haklı olarak gurur duyar. Diğer yandan, bazen, ilgi çekici soyutlamalara imkan sağlamak için olağandandan daha sihirli dil yapıları bulunan Python’un daha kirli ve anlaşılması güç kısımları deşilebilir. Metasınıflar böyle özelliklerdir.

Malesef, metasınıfların namı, "sorun arayan çözümler" olarak bilinir. Bu makalenin amacı, sıkça kullanılan Python kodlarının içerisinde, metasınıfların gerçek kullanımından birkaç örnek göstermektir.

İnternette Python metasınıflarıyla ilgili birçok materyal var (ÇN: Türkçe yok malesef), yani bu sadece metasınıflar üzerine başka bir ders değil ( Referanslar bölümünde faydalı bulduğum linklere bakabilirsiniz.). Metasınıfların ne olduğu konusuna biraz değineceğim fakat, benim amacım örnekler. Bunu söylemişken, bu makale kendi yağında kavrulmayı hedefler – metasınıfların ne olduğunu bilmiyorsanız bile okumaya başlayabilirsiniz.

Başlamadan önce bir diğer hatırlatma – bu makale Python 2.6 & 2.7’e göre yazılmıştır, çünkü internette bulacağınız birçok kod hala bu sürümler içindir. Python 3.x’de metasınıflar benzer şekilde çalışsa da bunları belirtmenin söz dizimi birazcık farklıdır. Sonuç olarak, bu makalenin büyük bir kısmı 3.x’e de uygundur.

Sınıflar da objedir

metasınıflar’ı anlamak için sınıflar hakkındaki bazı şeyleri açıklığa kavuşturmalıyız. Python’da herşey bir objedir. Sınıflar da dahil. Aslına bakarsanız, Python’da sınıflar birinci-sınıf objelerdir – çalışma anında yaratılabilir, parametre olarak gönderilebilir ve fonksiyonlardan döndürülebilir ve değişkenlere atanabilir. Sınıfların bu özelliklerini gösteren interaktif komut satırından bir örnek:

>>> def make_myklass(**kwattrs):
...   return type('MyKlass', (object,), dict(**kwattrs))
...
>>> myklass_foo_bar = make_myklass(foo=2, bar=4)
>>> myklass_foo_bar
<class __main__.MyKlass>
>>> x = myklass_foo_bar()
>>> x
<__main__.MyKlass object at 0x01F6B050>
>>> x.foo, x.bar
(2, 4)
  

Burada type yerleşik fonksiyonunun 3 argümanlı şeklini MyKlass isimli, object’den miras alan, bazı özellikleri argüman olarak sağlanmış bir sınıfı dinamik olarak oluşturmak için kullanıyoruz. Daha sonra böyle bir sınıf yaratabiliriz. Görebileceğiniz gibi, myklass_foo_bar şuna eşittir:

class MyKlass(object):
  foo = 2
  bar = 4
  

Ancak çalışma anında oluşturulmuş, fonksiyondan döndürülmüş ve bir değişkene atanmıştır.

Sınıf’ın sınıf’ı

Python’daki her obje (yerleşikler de dahil) bir sınıfa sahiptir. Biraz önce sınıfların da obje olduklarını gördük, yani sınıfların da bir sınıfı olmak zorunda, değil mi? Kesinlikle. Python __class__ özelliği ile bir objenin sınıfını incelememize izin verir. Bunu çalışırken görelim:

>>> class SomeKlass(object): pass
...
>>> someobject = SomeKlass()
>>> someobject.__class__
<class __main__.SomeKlass>
>>> SomeKlass.__class__
<type 'type'>
  

Bir sınıf ve bu sınıfın bir objesini yarattık. someobject objesinin __class__ özelliğini inceleyerek, bunun SomeKlass olduğunu gördük. İlginç kısım şimdi geliyor. SomeKlass’ın sınıfı ne? __class__ ile bunu tekrar inceleyebiliriz ve bunun type olduğunu görüyoruz.

Yani type, Python sınıflarının sınıfıdır. Diğer bir deyişle, yukarıdaki örnekteki someobject bir SomeKlass objesiyken, SomeKlass’ın kendisi de bir type objesidir.

Sizi bilmem ama ben bunu güven tazeleyici buluyorum. Madem sınıfların Python’da obje olduklarını öğrendik, bunların da bir sınıflarının olması mantıklı ve bir yerleşik sınıfın (type) sınıfların sınıfı rolünü oynaması güzel.

Metasınıf

Metasınıf "sınıfın sınıfı" olarak tanımlanır. Kendi örnekleri de bir sınıf olan her sınıfa metasınıf denir. Öyleyse, yukarıda gördüklerimize göre bu type’ı bir metasınıf yapar – aslında, sınıfların öntanımlı metasınıfı olduğu için en çok kullanılan metasınıftır.

Metasınıf bir sınıfın sınıfı olduğu için, sınıf oluşturmakta kullanılır (sınıfların obje oluşturmakta kullanıldığı gibi). Bir dakika, biz sınıfları standart class tanımıyla oluşturmuyor muyuz? Kesinlikle, ama Python merdiven altında şunları yapıyor:

  • class tanımı gördüğünde Python bunu çalıştırarak özellikleri (metotlar da dahil) bir sözlüğe toplar.
  • class tanımı bittiğinde Python sınıfın metasınıfını belirler. Şimdilik buna Meta diyelim.
  • Daha sonra Python Meta(name, bases, dct) deyimini çalıştırır ki burada:
    • Meta metasınıftır ki bunu çağırmak bir örneğini oluşturmaktır.
    • name yeni oluşturulan sınıfın ismidir.
    • bases sınıfın temel sınıflarının bir demetidir.
    • dct Sınıf’ın tüm özelliklerini listeleyerek özellik isimlerini objelerle eşletirir.

Bir sınıfın metasınıfını nasıl belirleriz? Basitçe eğer sınıf veya temelleri __metaclass__ özelliği tanımlıyorsa, bu metasınıf olarak alınır. Aksi takdirde metasınıf typedır.

O halde şunu tanımladığımızda olan nedir:

class MyKlass(object):
    foo = 2
  

Şudur: MyKlass’ın __metaclass__ özelliği yok, yani bunun yerine type kullanılıyor, ve sınıf oluşumu şu şekilde yapılıyor:

MyKlass = type(name, bases, dct)

Ki bu, makalenin başında gördüğümüzle tutarlı. Ancak diğer yandan eğer MyKlass bir metasınıf tanımlasaydı:

 class MyKlass(object):
  __metaclass__ = MyMeta
  foo = 2
  

O zaman sınıf oluşumu şöyle olurdu:

MyKlass = MyMeta(name, bases, dct)
  

O halde, MyMeta bu şekilde çağırılmayı destekleyecek ve bir sınıf döndürecek şekilde uygulanmalı. Aslına bakarsanız bu, önceden tanımlanmış yapıcı imzası olan normal bir sınıf yazmaya benzer.

Metasınıf’da __new__ ve __init__

Sınıfın metasınıf içinde oluşturulmasını ve ilklenmesini (ÇN: initialization) kontrol etmek için, metasınıf’da __new__ metodunu ve/veya __init__ yapıcısını uygulayabilirsiniz (ÇN: implement). Çoğu gerçek-hayat metasınıfları muhtelemen sadece bir tanesinin üstüne yazar.__new__ yeni obje yaratılmasını kontrol etmek istediğinizde (bizim durumumuzda bu bir sınıf) ve __init__ yeni obje yaratıldıktan sonra, ilklenmesini istediğinizde uygulanmalıdır.

Yani, MyMeta çağırıldığında, merdiven altında yapılan şey:

MyKlass = MyMeta.__new__(MyMeta, name, bases, dct)
MyMeta.__init__(MyClass, name, bases, dct)
  

Burada olup biteni göstermek için daha sağlam bir örnek var. Haydi bir metasınıf için şu tanımı yazalım:

class MyMeta(type):
    def __new__(meta, name, bases, dct):
        print '-----------------------------------'
        print "Sınıf için hafıza ayırılıyor", name
        print meta
        print bases
        print dct
        return super(MyMeta, meta).__new__(meta, name, bases, dct)
    def __init__(cls, name, bases, dct):
        print '-----------------------------------'
        print "Sınıf başlatılıyor", name
        print cls
        print bases
        print dct
        super(MyMeta, cls).__init__(name, bases, dct)
  

Python aşağıdaki class tanımını çalıştırdığında:

class MyKlass(object):
    __metaclass__ = MyMeta

    def foo(self, param):
        pass

    barattr = 2
  

Ekrana basılan şudur (güzel görünmesi için biraz düzenlenmiştir):

   -----------------------------------
Sınıf için hafıza ayırılıyor MyKlass
<class '__main__.MyMeta'>
(<type 'object'>,)
{'barattr': 2, '__module__': '__main__',
 'foo': <function foo at 0x00B502F0>,
 '__metaclass__': <class '__main__.MyMeta'>}
-----------------------------------
Sınıf başlatılıyor MyKlass
<class '__main__.MyKlass'>
(<type 'object'>,)
{'barattr': 2, '__module__': '__main__',
 'foo': <function foo at 0x00B502F0>,
 '__metaclass__': <class '__main__.MyMeta'>}
  

Bu örneği iyice anlayın ve metasınıf yazmakla ilgili bilinmesi gerekenlerin çoğunu kavrayacaksınız.

Bu ekrana yazdırmaların sınıf oluşturma anında yapıldığını hatırlatmak burada önem arz ediyor, yani, bu sınıfı içeren modülün içe aktarıldığı zamanda. Bunu aklınızın bir köşesine yazın.

Metaclass’da __call__

Bazen üzerine yazılması faydalı olabilecek bir diğer metasınıf metodu __call__’dur. Bunu __new__ ve __init__ metotlarında ayrı ele almamın nedeni, __call__ metodunun, bu ikilinin sınıf oluşturulması anınında çalışmasının aksine, zaten oluşturulmuş sınıfın, yeni bir obje örneklenmesi (ÇN:instantiate) için "çağırıldığında" çalıştırılması. İşte bunu açıklığa kavuşturmak için biraz kod:

class MyMeta(type):
    def __call__(cls, *args, **kwds):
        print '__call__ of ', str(cls)
        print '__call__ *args=', str(args)
        return type.__call__(cls, *args, **kwds)

class MyKlass(object):
    __metaclass__ = MyMeta

    def __init__(self, a, b):
        print 'MyKlass object with a=%s, b=%s' % (a, b)

print 'Şimdi foo oluşturacağım'
foo = MyKlass(1, 2)
  

Bu şunu yazdırır:

Şimdi foo oluşturacağım
__call__ of  <class '__main__.MyKlass'>
__call__ *args= (1, 2)
MyKlass object with a=1, b=2
  

Burada MyMeta.__call__ sadece bizi argümanlardan haberdar edip, işi type.__call__ metoduna devrediyor. Ama aynı zamanda işleme müdahale edip, sınıflardan nasıl obje yaratıldığını etkileyebilir. Bir bakıma, sınıfın kendisindeki __new__ metodunu yazmaya benzer, ancak yinede birkaç farkı vardır.

Örnekler

Şimdiye kadar metasınıflar nedir ve nasıl yazılır konularını anlamak için yeteri kadar teori işledik. Bu noktada, konuyu açacak örneklerin zamanı geldi. Yukarıda bahsettiğim gibi, sentetik örnekler yazmaktansa, gerçek Python kodu içerisindeki metasınıf kullanımını incelemeyi tercih ederim.

string.Template

İlk metasınıf örneği Python standart kütüphanesinden alınmıştır. Python ile birlikte gelen çok az sayıdaki metasınıf örneklerinin bir tanesidir.

string.Template kullanımı kolay, yazı doldurma (ÇN: string substitution) özelliği sağlar ve çok basit bir şablonlama sistemi görevi yapar. Eğer bu sınıfla aşina değilseniz, şimdi belgeleri okumak için güzel bir zaman. Ben sadece metasınıfları nasıl kullandığını açıklayacağım.

İşte class Template içerisinden ilk birkaç satır:

class Template:
    """A string class for supporting $-substitutions."""
    __metaclass__ = _TemplateMetaclass

    delimiter = '$'
    idpattern = r'[_a-z][_a-z0-9]*'

    def __init__(self, template):
        self.template = template
  

Ve bu da _TemplateMetaclass :

  class _TemplateMetaclass(type):
    pattern = r"""
    %(delim)s(?:
      (?P%(delim)s) |   # Escape sequence of two delimiters
      (?P%(id)s)      |   # delimiter and a Python identifier
      {(?P%(id)s)}   |   # delimiter and a braced identifier
      (?P)              # Other ill-formed delimiter exprs
    )
    """

    def __init__(cls, name, bases, dct):
        super(_TemplateMetaclass, cls).__init__(name, bases, dct)
        if 'pattern' in dct:
            pattern = cls.pattern
        else:
            pattern = _TemplateMetaclass.pattern % {
                'delim' : _re.escape(cls.delimiter),
                'id'    : cls.idpattern,
                }
        cls.pattern = _re.compile(pattern, _re.IGNORECASE | _re.VERBOSE)
  

Bu makalenin ilk kısmında sağlanan açıklama _TemplateMetaclass’ın nasıl çalıştığını anlamaya yeterli olmalı. Bunun __init__ metodu bazı sınıf özelliklerine bakar (özellikle pattern, delimeter ve idpattern) ve bunu kullanarak (veya kendi sağladığı öntanımlıları) derlenmiş bir düzenli ifade oluşturur ki, bu daha sonra sınıf’ın kendi pattern özelliğine depolanır.

Belgelerine göre, Template miras alınarak, özel delimeter ve ID yapısı, veya tam bir düzenli ifade sağlanabilir. Metasınıf sınıf oluşturma anında bunları derlenmiş düzenli ifadelere dönüştürür, yani bu bir nevi optimizasyondur.

Demek istediğim, aynı özelleştirme metasınıf kullanmadan, basitçe, derlenmiş düzenli ifadeyi __init__ içerisinde oluşturarak yapılabilir. Ancak, bunun anlamı, derlenme işleminin her Template objesi örneklendiğinde yapılacak olmasıdır.

Şu kullanımı düşünün, ki bu kendi dürüst fikrime göre string.Template için sıkça görülür:

>>> from string import Template
>>> Template("$name is $value").substitute(name='me', value='2')
'me is 2'
  

Düzenli ifade derlenmesini Template oluşturma zamanına bırakmak, böyle bir kod her çalıştığında bunun oluşturulması ve derlenmesi demektir. Bu bir ayıp, çünkü düzenli ifade şablondaki yazıya bağlı değil, sadece sınıfın özelliklerine bağlı.

Bir metasınıf ile sınıfın pattern özelliği modül yüklenip class Template (veya bir alt sınıfı) tanımı çalıştırıldığında oluşturuluyor. Bu Template objesi yaratıldığında zaman kazandırır, ve anlamlıdır çünkü sınıf oluşturma zamanında düzenli ifadeyi derlemek için gerekli tüm bilgilere sahibiz – öyleyse neden bekleyelim?

Bunun henüz olgunlaşmamış bir optimizasyon olduğu iddia edilebilir, ve bu doğru olabilir. Metasınıfın bu (veya herhangi bir) kullanımını savunmayı düşünmüyorum. Buradaki niyetim çeşitli görevler için metasınıfların gerçek kodlarda nasıl kullanıldığını sergilemek. Yani, eğitsel amaçlar için iyi bir örnek, çünkü ilginç bir kullanımı gösteriyor. Olgunlaşmamış bir optimizasyon veya değil, metasınıf bir hesaplamayı kod çalıştırma sürecinde bir adım önceye alarak kodu daha etkin hale getiriyor.

twisted.python.reflect.AccessorType

Aşağıdaki örnek metasınıfları gösterirken/anlatırken sıkça kullanılır. Bunun belgelerinden bir alıntı:

Kendiliğinden sınıf özellikleri üreten bir metasınıf. Bu metasınıfı kullanmak, sınıfınıza açık erişici metotları (ÇN: explicit accessor methods) sağlar; set_foo isimli bir metot, kendi kendine, set_foo metodunu setter olarak kullanan ‘foo’ özelliğini oluşturacak. get_foo ve del_foo için de aynı şekilde. (ÇN: bakınız)

İşte metasınıf, önemli kısımlara vurgu yapmak için biraz kısaltılmış olarak:

  class AccessorType(type):
    def __init__(self, name, bases, d):
        type.__init__(self, name, bases, d)
        accessors = {}
        prefixs = ["get_", "set_", "del_"]
        for k in d.keys():
            v = getattr(self, k)
            for i in range(3):
                if k.startswith(prefixs[i]):
                    accessors.setdefault(k[4:], [None, None, None])[i] = v
        for name, (getter, setter, deler) in accessors.items():
            # create default behaviours for the property - if we leave
            # the getter as None we won't be able to getattr, etc..

            # [...] some code that implements the above comment

            setattr(self, name, property(getter, setter, deler, ""))
  

Bunun yaptığı dümdüz bir işlem:

  1. Sınıfın get_ , set_ veya del_ ile başlayan tüm özelliklerini bul.
  2. Kontrol etmeyi hedefledikleri özelliklere göre sınıflandır (altçizgiden sonra gelen kısım)
  3. Böylelikle bulunan her getter, setter, deleter üçlüsü için
  1. Üçünün hepsinin var olduğundan emin ol, veya uygun öntanımlılar oluştur.
  2. Bunları sınıf içinde bir property olarak ayarla

Böyle bir metasınıf ne kadar faydalıdır? Söylemesi zor aslında. Twisted’ın kendisi bunu kullanmıyor ama bunu public API olarak sağlıyor. Eğer birçok property ile birkaç sınıf yazacaksanız, bu metasınıf sizi birçok kodlamadan kurtarabilir.

pygments Lexer ve RegexLexer

pygments kütüphanesi metasınıf kullanımının ilginç bir deyimini sunar. Bir temel sınıf özel bir metasınıf ile oluşturulmuş. Kullanıcı sınıfları bu temel sınıfdan miras alıp, metasınıf’ı yanında bir bonus olarak alırlar. Öncelikle LexerMeta metasınıfına bir bakalım. Bu Lexer için bir metasınıf olarak kullanılır – pygments içindeki lexerlar için temel sınıf:

class LexerMeta(type):
    """
    This metaclass automagically converts `analyse_text` methods into
    static methods which always return float values.
    """

    def __new__(cls, name, bases, d):
        if 'analyse_text' in d:
            d['analyse_text'] = make_analysator(d['analyse_text'])
        return type.__new__(cls, name, bases, d)
  

Bu metasınıf, analyse_text mesajının tanımını yakalayıp, bunu herzaman kayan noktalı bir değer döndüren statik bir metot’a çevirmek için __new__ metodunun üstüne yazar (make_analysator fonksiyonunun yaptığı budur.).

Burada __init__ yerine __new__ kullanımına dikkat edin. Neden __init__ kullanılmadı? Benim düşüncem, bunun sadece bir tercih meselesi olduğu – aynı etki __init__ metodunun üstüne yazarak da başarılabilirdi.

pygments’den ikinci örnek biraz daha karmaşık, ama daha önceki örneklerde görmediğimiz birkaç özelliği içerdiği için açıklama zahmetine değer. RegexLexerMeta için kod bir hayli uzun, bu yüzden ilgili kısmı bırakmak için kodu keseceğim:

 class RegexLexerMeta(LexerMeta):
    """
    Metaclass for RegexLexer, creates the self._tokens attribute from
    self.tokens on the first instantiation.
    """

    # [...] kesilen kısım

    def __call__(cls, *args, **kwds):
        """Instantiate cls after preprocessing its token definitions."""
        if not hasattr(cls, '_tokens'):
            cls._all_tokens = {}
            cls._tmpname = 0
            if hasattr(cls, 'token_variants') and cls.token_variants:
                # don't process yet
                pass
            else:
                cls._tokens = cls.process_tokendef('', cls.tokens)

        return type.__call__(cls, *args, **kwds)
  

Çoğunlukla, kod oldukça temiz; metasınıf tokens sınıf özelliğini inceliyor, ve bundan _tokens oluşturuyor. Bu sadece sınıfın oluşturulması sırasında yapılıyor. Burada özellikle ilgilendiğimiz iki şey var:

  1. RegexLexerMeta LexerMeta’dan miras alır, böylece bunun kullanıcıları da LexerMeta’nın sağladığı hizmetleri alır. Metasınıfların miras alınması, bunların Python’daki en güçlü dil yapılarından birisi olmasının nedenlerinden birisidir. Bunları sınıf dekoratörleriyle yanyana koyun mesela. Bazı basit işler için, sınıf dekoratörleri metasınıfların yerine geçebilir, ama metasınıfların miras ilişkisi kurabilmesi dekoratörlerin yapamaycağı birşeydir.
  2. process_tokendef hesaplamaları __call__ içerisinde yapılıyor – ve özel bir kontrol bunun sadece sınıfın ilk örneklenmesinde çalıştığından emin oluyor. (__call__’un kendisi tüm örneklenmelerde çalışsa da). Neden böyle yapılsın, bunu sınıf üretimi anında yapmak varken (metasınıf’ın __init__’inde mesela)? Bana öyle geliyor ki, bu bir çeşit optimizasyon olabilir. pygments bir çok lexer ile birlikte gelir, ama belirli bir kod için bunların sadece bir veya ikisini kullanmak isteyebilirsiniz. Sadece kullandığınız lexer yerine, neden kullanmadığınız lexerlara yükleme zamanı harcayasınız? Gerçek sebep bu olsa da olmasada, bence yine de bu, metasınıfların kafa yorulması gereken ilginç bir yönü – meta-işlerinin nerede ve ne zaman yapıldığını seçmenize olanak sağlayan muhteşem esnekliği.

Sonuç

Bu makaleyi yazmaktaki amacım Pythondaki metasınıfların nasıl çalıştığını açıklamak ve gerçek Python kodundan sağlam metasınıf kullanım örnekleri göstermekti. Metasınıfların kötü bir şöhreti olduğunu biliyorum, çoğu insan bunları olması gerektiğinden daha fazla sihirli olarak görüyor. Benim bu konudaki düşüncem, diğer dil yapılarında olduğu gibi, metasınıfların bir araç olduğu ve programcının sonuçta bunu doğru kullanmaktan sorumlu olduğu. Her zaman iş gören en basit kodu yazın, ama ihtiyacınız olanın metasınıflar olduğunu hissederseniz, bunları kullanmakta özgürsünüz.

Umarım bu makale sınıfların oluşturulması ve kullanılması için metasınıfların sağladığı esnekliği göstermiştir. Örnekler metasınıfların uygulanması ve kullanılmasında çeşitli yönlerini göstermiştir; __init__,__new__ ve __call__ metotlarının üstüne yazılması, metasınıfın miras alınması, sınıflara özellikler eklenmesi, obje metotlarının statik metotlara dönüştürülmesi ve gerek sınıf tanımında gerekse de örneklenme zamanında optimizasyonlar yapılması.

Python içinde metasınıfların kullanılmasının en dikkate değer örnekleri muhtemelen ORM (Object Relational Mapping) çatılarıdır, Django’nun modellerinde olduğu gibi. Gerçekten, bunlar metasınıfların neler yapabileceğini göstermekte güçlü örneklerdir, ancak ben bunları burada göstermemeye karar verdim çünkü onların kodları karmaşık ve birçok alana-özel detaylar metasınıfları sergilemek olan asıl amaca zarar verirdi. Diğer yandan, bu makaleyi okumuş olmakla, daha karmaşık örnekleri anlamak için gereken herşeye sahip oldunuz.

Ekleme: Eğer metasınıfların diğer ilginç örneklerini bulursanız lütfen bana bildirin. Daha fazla gerçek-hayat kullanımı görmekle bir hayli ilgileniyorum.

Referanslar

Python 2.x ve 3.x Arasındaki Bazı Farklılıklar

19 December 2011 Yaşar Arabacı
Python 2'den 3'e geçiş aşamasında, el altında bulundurmak için, en sık ihtiyaç duyulacağını düşündüğüm farklılıklar, ve 2'den 3'e geçme ipuçlarının bir listesinden oluşan kısa bir rehber oluşturma ihtiyacı hissettim. Benim gibi bu aşamada el altında bir geçiş rehberi bulundurmak isteyenlerle de paylaşmış olmak için, blogumdan yazayım dedim. Eksikleri farkettikçe buraya ekleyeceğim.

Bilgilerin kaynakları (Şimdilik tek kaynak): Python Belgeleri

Not: Eğer python öğrenmeye başlayacaksanız, ve hangi sürümden başlayacağınıza karar vermeye çalışıyorsanız, bu rehberin size pek bir faydası olmayacaktır. Python 2.6 veya 2.7 ile öğrenmeye başlayın. Ancak, kodlarınızı çalıştırırken python yorumlayıcısını "-3" anahtarıyla başlatın. Böylece, python 3'de değişen özelliklerle ilgili bilgi alabilirsiniz. Örneğin:

python -3 merhaba-dunya.py

Eğer python konusunda deneyimliyseniz, ve python 3'e geçiş yapacaksanız, okumaya devam edin.


"print artık bir fonksiyon"

# python 2.x
print "Python 2\'de böyle yazılıyordu."

# python 3.x
print("python 3\'de böyle yazılıyor.")

"""
sözlük metodları dict.keys(), dict.items() dict.values() artık liste değil, sözlük görünüm 
döndürüyor. Bunlar, sözlük değiştikçe dinamik olarak yenileniyor.
"""

# python 2.x
a = {1:"a",2:"b"}
b = a.keys()
print b
# ekrana [1,2] basar.
a[3] = "c"
print b
# ekrana [1,2] basar

# python 3.x
a = {1:"a",2:"b"}
b = a.keys()
print(b)
# ekrana [1,2] basar.
a[3] = "c"
print(b)
# ekrana [1,2,3] basar

# python 3 de dict.iterkeys() dict.iteritems() dict.itervalues() artık yok.
# Anladığım kadarıyla, bunlar yerine yukarıda bahsedilen yeni 
# fonksiyonları kullanacağız.

# map() ve filter() da artık iterator döndürüyor.

map(lambda x: x-2, [5,7,10]).append(15) # python 3 de bunu yapamazsınız!

# Python 3'de range bir iterator, xrange kaldırıldı.
# bkz: http://yasar.serveblog.net/post/python-range-ve-xrange/

# zip() de bir iteratör döndürüyor.

####
# Int ve long Objeleri
####

# Python 3'de long objesi yok, int objesinin alabileceği maksimum değer kalktı.
# Dolayısıyla, sys.maxint sabiti de yok!
# ONEMLI : sys.maxsize sabiti hala var!

# long int'lerin repr() metodu artık sonda bir L döndürmüyor. (GICIKTIM BUNA ZATEN!)

Python 3'de unicode() yok

Unicode objeleri, str objeleri, bytearray objeleri ve bunlar arasında işlem yapmak çok kaygan bir zemindi, python 3'de bu işe de bir el atıldı.

  • Python 3'de yazı ve byte var. Tüm yazılar unicode.
  • unicode objesi yok, python 3'de yazılar str objesi kullanıyor, ancak bunlar python 2 deki unicode'lar gibi.
  • Bir önceki madde dolayısıyla, u"..." da python 3'de yok!
  • İki önceki madde dolayısıyla unichr() fonksiyonu da yok. chr() fonksiyonu unichr() gibi oldu.
  • Yazılar ve byte'lar arasında işlem ve karşılaştırma yok.
  • yazı -> byte : str.encode() yada, bytes(str,encoding="bir kodlama")
  • byte -> yazı : byte.decode() yada, str(byte,encoding="bir kodlama")
  • byte ve yazı objelerinde yerinde değiştirme yapılamıyor (mutable değil), bunun için bytearray() var. byte kullanacağınız neredeyse her yerde bytearray kullanabilirsiniz.

Python 3'de except ve raise

Hata yakalama ve ayıklama konusunda da bazı değişiklikler oldu. En önemli değişiklik, artık raise ile kullanacağınız exception, doğrudan veya dolaylı olarak BaseException sınıfının bir alt sınıfı olmak zorunda. Python 3'e geçiş yaparken, eğer BaseException sınıfının alt sınıfı olmayan herhangi birşeyi raise ile kullanıyorsanız, bunları baştan yazmanız gerekecek. Ancak halen tavsiye edilen, kendi hatalarınızı Exception sınıfının bir alt sınıfı olarak yazmanız. Bununla birlikte, yazımda da değişiklikler oldu.

# python 2.x
raise Hata, arguman
# python 3.x
raise Hata(arguman)

# python 2.x
except Hata, degisken:
    print(degisken.ozellik)
# python 3.x
except Hata as degisken:
    print(degisken.ozellik)

reduce() callable() gibi fonksiyonlar yok.

Hepsini yazmak uzun olacak diye, önemli bulduklarımı yazdım.

  • dict.has_key(a) kullanımı kalktı, bunun yerine "a in dict" kullanmak gerekiyor.
  • callable(a) kalktı. Bunun yerine issubclass(a,collections.Callable). Bunun hakkındaki şahsi görüşüm saçmalığın daniskası olduğu yönünde.
  • reduce() fonksiyonu da kalktı. Bunun yerine for döngüsü kullanın diyorlar. İlla ki reduce kullanmak isteyenler functools.reduce() kullanacaklarmış.
  • raw_input(), input() olarak adlandırıldı. Eski input davranışı için eval(input()) kullanın deniliyor.

Burada yazılanlar tüm değişiklikleri kapsamıyor. Sadece, kendi en sık kullandığım python özelliklerini göz önünde bulundurarak, değişiklikleri özet geçtim. Buraya eklenmesi gereken birşey olduğunu düşünüyorsanız, yorumlarda belirtebilirsiniz.

Son bir not olarak, python 3'e geçiş için henüz çok erken olduğunu düşünüyorum. Halen python 3'ün ne olduğu belirsiz ve aktif geliştirilmesi devam ediyor. Ben şahsen, 3.5 gibi geçmeyi düşünüyorum.

Python'da Birim Testi ve Test Temelli Geliştirme

17 December 2011 Yaşar Arabacı
Yazılım geliştirmede birim testi, kısaca, yazılımı oluşturan birimlerin belirlenen girdiler ve beklenen çıktılar kullanılarak, programatik olarak test edilmesidir. Birim ile kastedilen olgu bahsi geçen konuya göre değişebilse de, genelde, bir programı oluşturan en küçük parçadır. Test temelli geliştirme ise, birimi geliştirmeye başlamadan önce, bu birimden beklenenlerin belirlenip, beklentiye uygun testler yazılmasıdır. Daha sonra, tüm testler başarılı oluncaya kadar birimin geliştirilmesine devam edilir.

Birim testlerinin ve test temelli yazılım geliştirmenin birkaç avantajı vardır. dive into python'da bu avantajları çok iyi anlatmış, orayı çevirmekle yetineceğim:

  • Kod yazmadan önce, sizi gereksinimlerinizi faydalı bir şekilde detaylandırmaya zorlar.
  • Kod yazarken, sizi gereksiz kod yazmaktan kurtarır. Tüm testler başarılıysa, kod yeterli demektir.
  • Kodları yenilerken, yeni sürümün eskisi gibi davrandığından emin olmanızı sağlar. (Benim en sevdiğim özellik!)
  • Kodların bakımını yaparken, son yaptığınız değişiklik eski kodu bozdu diye size bozuk atanlara karşı kendinizi savunmanızı sağlar.("Ama birim testleri başarılı oldu...")
  • Bir ekip çalışması yapıyorsanız, kodlarınızın diğer geliştiricilerin kodlarını bozmadığından emin olmanızı sağlar. Değişikliklerinizi onaylayıp, ortak depolara yollamadan önce diğer geliştiricilerin testlerini uygulayabilirsiniz.

Python'da birim testi yapmak için, unittest modülü kullanılır. Testler unittest modülündeki TestCase sınıfının bir alt sınıfında yazılır. Bu alt sınıfın, adı test ile başlayan tüm metotları, farklı bir test tanımlar. Bu metotların içerisinde, TestCase sınıfının belirli metotlarıyla, birimin beklenen davranışı sergileyip sergilemediği test edilir.Python Belgelerinin unittest ile ilgili bölümü'nde belirtilen bu metotlar şunlardır:

Metot Neyi Test Eder Gerekli Versiyon
assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a, b) a is b 2.7
assertIsNot(a, b) a is not b 2.7
assertIsNone(x) x is None 2.7
assertIsNotNone(x) x is not None 2.7
assertIn(a, b) a in b 2.7
assertNotIn(a, b) a not in b 2.7
assertIsInstance(a, b) isinstance(a, b) 2.7
assertNotIsInstance(a, b) not isinstance(a, b) 2.7

2.7'de eklenen özellikler, Python 2.3'den sonrası için unittest2 modülüyle sağlanıyor. Bu modülü ayrıca indirip kurarak bu özellikleri de kullanabilirsiniz. Yukarıda bahsedilen metotlara ek olarak, şu iki metot da mevcuttur.

Metot Kontrol eder Gerekli Versiyon
assertRaises(exc, asdf, *args, **kwargs) asdf(*args, **kwargs) exc hatası verir.
assertRaisesRegexp(exc, re, asdf, *args, **kwargs) asdf(*args, **kwargs) exc hatası verir, ve hata mesajı re düzenli ifadesiyle eşleşir 2.7

assertRaisesRegexp metodunu 2.7 öncesi versiyonlarda yine unittest2 ile kullanabilirsinz.

Şimdi örnek birkaç test göstermek istiyorum. Öncelikle, neyi test edeceğimize karar vermemiz gerek. Benim aklıma bir karakter dizgesi olarak 4 işlemden oluşan bir matematiksel ifadeyi alan ve sonucu döndüren bir fonksiyon örneği yapmak geldi. Testleri yazmaya başlamadan önce, bu fonksiyondan ne beklediğimi kesinleştirmem gerekiyor.

  • Matematiksel ifadenin sonucunu doğru vermeli :)
  • Fonksiyonumun boşluklara takılmamasını, boşluklu veya boşluksuz çalışmasını istiyorum
  • Çarpma ve bölmenin işlem önceliğine dikkat etmeli.
  • Negatif sayıları dikkate almalı.
  • Yanlış bir ifadeyle çağırılırsa hata vermeli.
  • Mantıksal olarak, toplama ve çarpma işlemlerinde ifadelerin yerinin değişmesi sonucu değiştirmemeli

Artık istediklerimizi bildiğimize testlerimizi yazmaya başlayabiliriz. Önce unittest modülünü içe aktarıp, TestCase sınıfından, yeni bir sınıf türetmeliyiz. (alt sınıf oluşturmalıyız.)

import sys
from random import randint
### En güzel unittest'i bulmaya çalış :)

if sys.version_info[0] == 2 and sys.version_info[1] >= 3 and sys.version_info[1] < 7:
    try:
        import unittest2 as UnitTest
    except:
        import unittest as UnitTest
else:
    import unittest as UnitTest


class HesaplayıcıTestleri(UnitTest.TestCase):
    pass

Ben platforma uygunluk konusunda biraz takıntılı olduğum için çok uzattım. Size isterseniz basit bir "import unittest" ile unittest modülünü içe aktarabilirsiniz. Şimdi ilk testimizi yazalım. Bu ilk test aptal testi olacak. Fonsiyonumuz aptal mı onu test edeceğiz :)

# unittest içe aktarma burada olacak
# sonra da hayali modülü dahil etmeliyim. Bunu daha yazmadım :)
import Hesaplayici
class HesaplaTestler(UnitTest.TestCase):
    def testAptal(self):
        "Aptal testi"
        self.assertEqual(Hesaplayici.hesapla("2+2"),4)

Burada dikkat edilmesi gerek tek şey, metod ismine test ile başlamış olmam. Metodun çalışması için, isminin test ile başlaması gerekiyor. Metod başlangıcının hemen ardından yazılan bir satır açıklama da önemli. Eğer testler başarısız olursa, açıklamalar içerisinde size o bir satırlık açıklamayı da gösteriyor. Örneğin, "2+2" ile 5 eşit mi diye sorsak (unittest.main() çağrısı, mevcut bütün testlerin çalışmasına neden olacaktır.), şuna benzer bir çıktı verecekti.

F
======================================================================
FAIL: testAptal (__main__.HesaplaTestler)
Aptal Testi
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test2.py", line 19, in testAptal
    self.assertEqual(Hesaplayici.hesapla("2+2"),5)
AssertionError: 4 != 5

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)

Tek tek açıklamalara girmeden, bendeki testlerin son halini buraya yapıştırıyorum. Satır aralarındaki açıklamaların yeterli olacağını düşünüyorum.

# -*- coding: utf-8 -*-
import sys
from random import randint
### En güzel unittest'i bulmaya çalış :)

if sys.version_info[0] == 2 and sys.version_info[1] >= 3 and sys.version_info[1] < 7:
    try:
        import unittest2 as UnitTest
    except:
        import unittest as UnitTest
else:
    import unittest as UnitTest


import Hesaplayici

class HesaplaTestler(UnitTest.TestCase):
    
    def testYanlisGirdi(self):
        "Yanlış girdilerde hata veriyor mu?"
        yanlis_girdiler = [" * 12 + 15", "/ 3 - 6", "3 * 6 *","5 // 3","6 ** 7", "2 /* 5","15+++17"]
        for girdi in yanlis_girdiler:
            self.assertRaises(Hesaplayici.kotuGirdi,Hesaplayici.hesapla,girdi)
            
    def testSagSolOnemsiz(self):
        "çarpma ve toplamada, işleme girenlerin sayıların sırası sonucu değiştirmemeli :)"
        
        # bin kere deneyelim.
        for i in range(0,1000):
            sayi1 = str(randint(0,1000))
            sayi2 = str(randint(0,1000))
            
            self.assertEqual(Hesaplayici.hesapla(sayi1 + "+" + sayi2),Hesaplayici.hesapla(sayi2 + "+" + sayi1))
            self.assertEqual(Hesaplayici.hesapla(sayi1 + "*" + sayi2),Hesaplayici.hesapla(sayi2 + "*" + sayi1))
            
    def testbastaEksiveArti(self):
        "Basta eksi veya artı varken, düzgün çalışmalı"

        islemler = {
            "+2 + 5": 7,
            "-2 + 5": 3,
            "+2 * 10": 20,
            "-2 * 10": -20,
            "-10 / 5": -2,
            "-5 / -1": 5,
        }
        
        for islem,sonuc in islemler.items():
            self.assertEqual(Hesaplayici.hesapla(islem),sonuc)
        
    def testIslemOnceligi(self):
        "Çarpma ve bölme işlemlerinin önceliği vardır!"
        
        islemler = {
            "3 + 2 * 5": 13,
            "2 * 6 + 10": 22,
            "50 / 5 - 2": 8,
            "2 * 10 + 3 * 5": 35
        }
        
        for islem,sonuc in islemler.items():
            self.assertEqual(Hesaplayici.hesapla(islem),sonuc)

if __name__ == "__main__":
    UnitTest.main()

Yukarıdaki basit örnekler, kendi testlerinizi yazmaya başlamak için yeterli olacaktır. Artık elinizde testler olduğuna göre, fonksiyonunuzu yazmaya başlayabilirsiniz. Yapmanız gereken, kodları yazdıkça teste tabi tutmak, ve tüm testler başarılı oluncaya kadar kodları yenilemek. İleride bu fonksiyona yeni özellikler eklemeye çalıştığınızda bu testler çok işe yarayacak. Eğer yeni eklediğiniz özellikler, eski özelliklere zarar veriyorsa, testler hata vererek size bunu bildirecek. Siz de hatayı düzeltebileceksiniz.

Bu da benim bu testlere göre yazmış olduğum fonksiyon. Bende tüm testlerden geçti.

# -*- coding: utf-8 -*-
import re

class kotuGirdi(Exception):
    def __unicode__(self):
        return "Yanlış girdi kullandınız!"
        
    def __str__(self):
        return self.__unicode__()
    
    
def hesapla(string):
    # Önce boşlukları temizle
    string = string.replace(" ","")
    
    # girdi doğru mu?
    # basta "-" veya "+" opsiyonel
    # sonra bir sayı
    # sonra, sayılar ve islemler devam ediyor
    # son olarak da bir sayı var
    
    if not re.match("^[-+]?\d[-+*/\d]+\d$",string):
        raise kotuGirdi
        
    # İşlem operatöründen sonra, * veya / olamaz -> 5 +* 2
    if re.search("[-+*/][*/]+",string):
        raise kotuGirdi
        
    # Yanyana ikiden fazla işlem operatörü olamaz
    # İki tane olması mümkün -> 10 / -3, 
    
    if re.search("[-+*/]{3,}",string):
        raise kotuGirdi
    
    # Sayılarla islecleri ayıralım:
    sayilar = []
    islecler = []
    son_tip = "islec"
    gecici = ""
    
    for i in range(0,len(string)):
        
        # son karşılaştığımız bir sayıysa
        if son_tip == "sayi":
            
            if re.match("\d",string[i]):
                gecici += string[i]
            else:
                sayilar.append(int(gecici))
                islecler.append(string[i])
                son_tip = "islec"
        elif son_tip == "islec":
            
            # en son bir işleç gördüysek, yeni bir sayı oluşturmaya başlayabiliriz.
            gecici = string[i]
            son_tip = "sayi"
    
    # En son gecicide kalan sayıyı da ekleyelim
    
    sayilar.append(int(gecici))
    
    # İşlem tanımları
    islemler = {
    "+" : lambda x,y : x + y,
    "-" : lambda x,y : x - y,
    "*" : lambda x,y : x * y,
    "/" : lambda x,y : float(x) / y
    }
    
    # çarpma ve bölme işlemi önceliği
    
    while "*" in islecler or "/" in islecler:
        for i in range(0,len(islecler)):
            if islecler[i] in ["*","/"]:
                islem = islemler[islecler[i]]
                
                sonuc = islem(sayilar[i],sayilar[i+1])
                sayilar[i:i+2] = [sonuc]
                islecler = islecler[:i] + islecler[i+1:]
                break
                
    # Kalan toplama çıkarmalar:
    
    sonuc = sayilar[0]
    
    for i in range(1,len(sayilar)):
        islem = islemler[islecler[i-1]]
        sonuc = islem(sonuc,sayilar[i])
    return sonuc

HTMLParser ve urllib ile Web Sayfalarından Bilgi Almak

11 October 2011 Yaşar Arabacı

Bu yazıda, python kullanarak, internet sayfalarını okumak ve bu sayfalardan ilgilendiğimiz bilgileri toplamakla alakalı küçük bir örnek yapacağım. Örneğimizde, internetten bir html belgesi alacak ve bu html belgesindeki javascript'leri bir listeye toplayacağız.

Eğer daha önce bu konuda bir uğraşınız olmuşsa, neden SGMLLib değil de, HTMLParser kullandığımı merak edebilirsiniz. Bunun nedeni, python 3 ile birlikte, SGMLLib'in standard python modülleri içerisinden çıkarılması. HTMLParser modülü de html.parser modülüne taşındı, ancak o sorunu şu şekilde halledeceğim:

# -*- utf-8 -*-
from sys import version_info
if version_info[0] == 2 and version_info[1] >= 2:
    from HTMLParser import HTMLParser
elif version_info[0] == 3:
    from html.parser import HTMLParser
else:
    from sys import exit
    print("Python yorumlayıcınız bu programı kullanamaz!")
    exit(1)

HTMLParser sınıfının bir alt sınıfını oluşturmadan önce, bu sınıfın nasıl çalıştığını bir örnekle açıklamak istiyorum. Aşağıdaki basit html belgesine bir göz atalım:

<html>
     <head>
          <title>Başlık</title>
     </head>
     
     <body>
          <span id="benimspan" class="7b"></span>
     </body>
</html>

HTMLParser sınıfı bu belgeyi ayrıştırmak için, sırasıyla aşağıdaki metotları parantez içerisinde gördüğünüz argümanlarla çağıracak:

  • HTMLParser.handle_starttag("html",[])
  • HTMLParser.handle_starttag("head",[])
  • HTMLParser.handle_starttag("title",[])
  • HTMLParser.handle_data("Başlık")
  • HTMLParser.handle_endtag("title")
  • HTMLParser.handle_endtag("head")
  • HTMLParser.handle_starttag("body",[])
  • HTMLParser.handle_starttag("span",[("id","benimspan"),("class","7b")])
  • HTMLParser.handle_endtag("span")
  • HTMLParser.handle_endtag("body")
  • HTMLParser.handle_endtag("html")

Ancak, bahsedilen metotların hiçbiri, birşey yapmıyor. HTMLParser sınıfının bir işe yaraması için, bu sınıfın bir alt sınıfını oluşturarak, programlama mantığınızı bu alt sınıfta uygulamanız gerekiyor. Aşağıda benim yaptığım örnek var:

# -*- coding: utf-8 -*-
from sys import version_info
if version_info[0] == 2 and version_info[1] >= 2:
    from HTMLParser import HTMLParser
elif version_info[0] == 3:
    from html.parser import HTMLParser
else:
    from sys import exit
    print("Python yorumlayıcınız bu programı kullanamaz!")
    exit(1)
    

class ScriptAl(HTMLParser):
    
    def reset(self):
        self.scriptler = []
        self.script_ici = False
        HTMLParser.reset(self)
        
    def handle_starttag(self,tag,ozellikler):
        
        if tag == "script":
            for anahtar,deger in ozellikler:
                if anahtar == "type" and deger == "text/javascript":
                    self.script_ici = True
                    
    def handle_endtag(self,tag):
        if tag == "script" and self.script_ici:
            self.script_ici = False
            
    def handle_data(self,data):
        if self.script_ici:
            self.scriptler.append(data)

İlk 10 satırda önemli birşey yok, farklı python yorumlayıcılarını desteklemek için import çağrısını biraz uzattım o kadar. 13. satırda, asıl işi yapacak sınıfımı oluşturdum. Bu sınıf, HTMLParser sınıfının bir alt sınıfı. Burada, HTMLParser sınıfının istediğim metotlarını uygulayarak, kendi sınıfımın yapısını oluşturacağım. reset metodu HTMLParser tarafından, yeni bir belge ayrıştırılmaya başlamadan önce çağırılıyor. Burada, kullanmak istediğiniz değişkenleri sıfırlamanızı tavsiye ederim. Daha sonra da HTMLParser.reset(self) ile bu metodun HTMLParser sınıfındaki davranışını tekrar etmesini sağlayabilirsiniz.

Örnekde programlama mantığım çok basit. Bir script etiketi açıldığında, script_ici değişkenini True'ya çeviriyorum. Etiket kapandığı zaman da bu değişkeni False'a geri çeviriyorum. handle_data metodunda ise, eğer bir script içerisindeysek, verileri listeye ekliyorum. Aşağıdaki örnekde, bir sayfadaki javascriptlerin hepsini alıp, bunları bir dosyaya yazacağız. Yukarıdaki kodların scriptal.py isminde bir dosyada olduğunu, ve bu dosyaların import edebileceğimiz bir yerde olduğunu varsayın.

from scriptal import ScriptAl
import urllib

parser = ScriptAl()
soket = urllib.urlopen("http://tr.myspace.com")
parser.feed(soket.read())
soket.close()

dosya = open("scriptler.txt","w")
for script in parser.scriptler:
    dosya.write("<script type=\"text/javascript\">\n")
    dosya.write(script)
    dosya.write("</script>\n\n")
dosya.close()

Örneğim, bu kadar.İyi geliştirmeler.

Benzer Yazı Analizi 2

09 October 2011 Yaşar Arabacı

Benzer Yazı Analizi 1 başlıklı yazıyı okuduysanız, iki yazı arasındaki benzerlikleri hesaplamaya çalışan bir algoritma yazmaya çalışıyordum ancak çok da başarılı olamamıştım. Bu yazıda, algoritmayı biraz geliştirdim ve aldığım sonuçlar tatmin edici oldu.

Bu yeni algoritmanın en önemli farkı, her kelimeye kendine göre katsayı ataması. Benzer Yazı Analizi 1 yazısında bahsettiğim, Text similarity: an alternative way to search MEDLINE yazısını takip etmeye devam ettim, ve oradaki algoritmayı aynen uygulamaya çalıştım. Kodlar aşağıda:

# -*- coding:utf-8 -*-
from django.utils.html import strip_tags

import os
import sys
import math
import re
PROJE_DIZINI = os.path.abspath(os.path.dirname(__file__))
UST_DIZIN = os.path.abspath(PROJE_DIZINI + "/../")
sys.path.append(UST_DIZIN)

os.environ["DJANGO_SETTINGS_MODULE"] = "similarity.settings"


from blog.models import Post




def kelimeleriAl(post_objesi):
    tumu = post_objesi.title + " " + post_objesi.abstract + " " + post_objesi.post
    
    tumu = strip_tags(tumu.lower())
    
    regex = re.compile("\W+",flags=re.UNICODE)
    return re.split(regex,tumu)
    
def say(sayilacak,kume):
    sayi = 0
    for obje in kume:
        if sayilacak in obje[1]:
            sayi += 1
    return sayi
    
a = Post.objects.all()
b = []
for post in a:
    b.append((post.title,kelimeleriAl(post)))
del(a)

def katsayiAyarla(liste,kelime):
    kacKere = liste.count(kelime)
    if kacKere == 0:
        return 0
    else:
        return math.log(kacKere,1.6)
        

liste = []
tekil = []
for i in range(0,len(b)):
    for kelime in b[i][1]:
        if kelime not in tekil:
            tekil.append(kelime)
            
for i in range(1,len(b)):
    
    for j in range(0,i):
        
        ust_taraf = 0
        toplam1 = 0
        toplam2 = 0
        
        for kelime in tekil:
            
            # Kelime önemine göre ayarlanmış katsayılar
            ayarli1 = katsayiAyarla(b[i][1],kelime)
            ayarli2 = katsayiAyarla(b[j][1],kelime)
            
            ust_taraf +=  ayarli1 * ayarli2 * math.log(len(b)/say(kelime,b))
            toplam1 += ayarli1
            toplam2 += ayarli2
            
            
        alt_taraf = math.sqrt(toplam1 * toplam2)
        
        liste.append((b[i][0], b[j][0], ust_taraf/alt_taraf))
        
liste = sorted(liste, key = lambda x: x[2])
liste.reverse()
print("\n".join(["%s and %s => %f" % (x,c,v) for x,c,v in liste]).encode("utf-8"))
        

Bu algoritmada, kelimeleriAl fonksiyonunu, sadece tekil kelimeleri değil, tüm kelimeleri geri döndürecek şekilde yeniden ayarladım. Bu yeni algoritmada, eskisinin aksine, vektör oluştururken (vektörleri değişkenlere atamaktansa, doğrudan döngü içerisinde hesabı yaptım), her kelimenin karşılaştırılan yazılarda kaçar kere geçtiğini ve bu kelimenin önemini de hesaba katıyoruz. Bunu yapmak için, karşılaştırılan kelimenin, site içerisinde kaç farklı makalede bulunduğuna bakıyoruz. Ne kadar yaygın bir kelimeyse, o kadar düşük bir öneme sahip olacak. Çünkü, "ve" "ile" vb. bağlaçlar ve bunlar gibi sık tekrar edilen kelimeler, tanımlayıcı özelliklerini kaybediyor. Bunu 72. satırdaki math.log(len(b)/say(kelime,b)) ile yapıyoruz. len(b) ile tüm makalelerin sayısını alıp, sonra say fonksiyonunda bu kelimenin kaç makalede geçtiğine bakıyoruz. Son olarak, bunların oranının doğal logaritmasını alarak katsayıyı oluşturuyoruz. Örneğin bu katsayı,bütün makalelerde geçen kelimeler için 0 olacak, dolasıyla her makalede geçen kelimeler, makalelerin benzerliğini hesaplarken göz ardı edilecek. Bu ince ayarlar, algoritmayı kosinüs yöntemiyle benzerlik bulmakdan biraz uzaklaştırıyor, ancak, sonuçlarda bariz şekilde bir düzelme sezdim.

Karşılaştırdığımız kelimelerin her iki makaledeki sayısının önemli olduğunu belirtmiştik, ancak, belli bir yerden sonra kelimenin tekrar etmesinin çok önemi olmamalı. Bunu 47. satırdaki aldığımız 1.6'ya göre logaritma sayesinde yaptık. Böylece, 5-6 kadar tekrardan sonra, bir kelimenin ne kadar tekrar ettiğinin önemi çok düşük kalmış oldu.

Algoritmanın bölünen kısmını bir örnekle açıklamak gerekebilir. Diyelim ki, "python" kelimesi karşılaştırılan yazıların birisinde 3 kere, diğerinde 5 kere geçiyor. Ve sitedeki 10 makaleden 7'sinde python var. Böylece, algoritmanın bölünen kısmı, log1.6(3) * log1.6(5) * log(10/7) = 2.33 * 3.42 * 0.35 = 2.85 olacak. Bölen kısmına da örnek verelim. Karşılaştırdığımız yazıların birinde 3 kere python, diğerinde ise 5 kere python yazdığını varsayayım. Yani, tek ilgilendiğimiz kelime python kelimesi, o yüzden alt taraf, 2.33 * 3.42 = 8 olur. Böylece, 3 kere python ile 5 kere python yazan iki makale arasındaki sonuç yaklaşık 0.25 olucak.

Aşağıda, bu site için yaptığım analizin bir kısmını görebilirsiniz. Bence bu sonuçlar oldukça tatmin edici oldu.

Django'da Url Taşıma and 0'dan Bloga Django(4) => 1.000976
0'dan Blog'a Django (5) and Django'da Url Taşıma => 0.771432
Django Modelleriyle Paket Yöneticisi and 0'dan blog'a Django(1) => 0.749185
If..Else yada Try..Except, hangisi ne zaman kullanılmalı? and Django ve Url Düzeltme => 0.682939
0'dan Bloga Django(3) and 0'dan Blog'a Django(2) => 0.671333
0'dan Bloga Django(4) and 0'dan Bloga Django(3) => 0.618953
0'dan Blog'a Django (5) and 0'dan Bloga Django(4) => 0.616938
Django ve Url Düzeltme and 0'dan Blog'a Django (5) => 0.552183
Django ve Url Düzeltme and 0'dan Bloga Django(4) => 0.532040

En yüksek benzerlik, Django'da url taşıma ve 0'dan bloga django 4 yazıları arasında çıktı. Eğer bu yazılara bakarsanız, ikisinin de url yapılandırmasından bahsettiğini göreceksiniz. 0'dan Bloga Django yazı dizisindeki tüm yazıların da birbirileriyle benzerlikleri yüksek çıktı.

Sonuç olarak, bu algoritmayı tatmin edici buldum. Birkaç gün içerisinde optimizasyonlarını yapar siteye eklerim diye düşünüyorum

İyi Geliştirmeler.

Django'da Türkçe Destekli Slugify

05 October 2011 Gökmen Görgen

Wordpress'te "Tanrı Yanılgısı" diye bir başlık attığınızda, WordPress'in sizin için urettiği URL'e bakalım:

http://blog.gokmengorgen.net/2010/09/09/tanri-yanilgisi/

Tam istediğimiz gibi. Django ile bir blog uygulaması yazıyor olsaydım, sanırım biraz sıkıntı çekecektim:

In [1]: from django.template.defaultfilters import slugify
In [2]: slugify('Tanrı Yanılgısı')
Out[2]: u'tanr-yanlgs'

Gitti ı'lar... Turkçe karakterler arasından Django'nun slugify'sinde çalışmayan tek karakter kuçuk harfle yazılmış 'ı'. Baştan slugify fonksiyonuna bir yama yapıp Django ekibine gondermeyi duşundum; fakat sadece Turkçe'yi dikkate aldığım için buyuk ihtimalle yamam reddedilecekti. Ben de bir utils.py dosyası açıp şoyle bir slugify fonksiyonu yazdım:

1
2
3
4
5
6
def slugify_unicode(value):
    value = value.replace(u'\u0131', 'i')
    value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
    value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())

    return mark_safe(re.sub('[-\s]+', '-', value))

Bu fonksiyonu iPython'da denediğinizde patlıyor; ama sitede kullandığınızda herhangi bir sorunla karşılaşılmıyor. Bunun sebebi, 'ı' harfinin iPython konsolunda u0131 olarak donmemesinden kaynaklanıyor:

In [5]: u"Tanrı Yanılgısı" Out[5]: u'Tanr\xc4\xb1
Yan\xc4\xb1lg\xc4\xb1s\xc4\xb1'

Bu bana Python'un garipliği gibi geliyor ve Python3'te sorunun duzeltildiğini soyluyorlar (Python3'te unicode default olacak). Henuz denemedim. Emin olmamakla birlikte, slugify fonksiyonu için değer olarak verdiğim başlığın Django'dan "Tanrı Yanılgısı" olarak değil de, "Tanru0131 Yanu0131lgu0131su0131" çekilmesinden kaynaklanıyor:

In [24]: x = u"Tanr\u0131 Yan\u0131lg\u0131s\u0131"
In [25]: x
Out[25]: u'Tanr\u0131 Yan\u0131lg\u0131s\u0131'
In [26]: slugify_unicode(x)
Out[26]: u'tanri-yanilgisi'

Django'da Fabric Kullanımı

05 October 2011 Gökmen Görgen

Bir Django projesinde statik dosyaları bir dizinde toplayıp, sunuma hazır hale getirmek için kaç satır komut giriyorsunuz? Bu tip durumlarla baş etmek için küçük bir betik yazıp, o betiği python deploy.py collectstatic gibi belirli argümanlarla çalıştırarak üstesinden gelmek, her projeyi güncellerken tekrar tekrar aynı komutları girmekten daha pratik olabiliyor.

Daha pratiği, fabric kullanmak. Fabric, bir web projesini sunmak için gerekli komutları yazabileceğimiz, argümanları kolayca tanılayabileceğimiz, gerektiğinde kendi bilgisayarımızdan uzaktaki sunucuya bağlanmasını sağlayabileceğimiz bir kitaplık ve konsol uygulamasıdır. Django belgelerinde, fabric ile uzaktaki sunucuya erişim statik dosyaları sunuma hazır hale getiren örnek bir fonksiyon bulunuyor:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from fabric.api import *

# Hosts to deploy onto
env.hosts = ['www1.example.com', 'www2.example.com']

# Where your project code lives on the server
env.project_root = '/home/www/myproject'

def deploy_static():
    with cd(env.project_root):
    run('./manage.py collectstatic -v0 --noinput')

Bu kodu proje dizini içinde fabfile.py veya fabfile/__init__.py'ye yazmak gerekiyor. Eğer statik dosyaları sıkıştırmak, coffeescript'i javascript koduna çevirmek gibi statik dosyalarınız ile ilgili daha başka işlemleriniz varsa, deploy_static() fonksiyonu altında bunların hepsini yazabilirsiniz. Daha sonra konsoldan proje dizinine girip şu komutu vermeniz yeterli olacak:

fab deploy_static

Sadece Django değil, sunucunuzda sunacağınız herhangi bir web çatısı, cms veya uygulamada fabric kullanmak, konsol araçları yazmakta kolaylık sağlayabilir. Hatta Fabric'i kullanacağınız projenin ana dilinin Python olmasına bile gerek yok. Yeter ki bir konsol aracına ihtiyacınız olduğu an, Fabric'e bir göz atın.

Nodejs & CoffeeScript ile yazılmış bir web çatısı: Juju

05 October 2011 Gökmen Görgen

İlk kez bir programlama dilini web çatısı yazarak öğrenmeyi denedim, gerçekten eğlenceli olduğunu gördüm. Planladığım şey, çok sık kullandığım bir web çatısı olan Django'yu, öğrenmek istediğim dilde bir benzerini yazmaktı. Elbette Django gibi devasa bir çatıyı hemencecik yazmak mümkün değil; ama bir prototipini yazmayı denedim, pişman da olmadım. Hatta devamını bile getirebilirim.

Yalnız işe girişirken Django'da anlam veremediğim birtakım şeyler yok değil, mesela:

  • Neden proje oluşturmak için django-admin.py, proje oluşturduktan sonraki komutlar içinse ./manage.py kullanıyoruz? İkisi için de django diye bir komut olamaz mı?
  • Uygulama dizinleri ile diğer dizinleri neden birbirinden ayırmıyoruz?
  • Neden view'ları fonksiyon yerine class kullanarak yazmıyoruz? (Bu konuyla ilgili yazılan makaleye göre, seçimini fonksiyonlardan yana yapan insanlar da varmış.)
  • Neden statik dosyaların bulunduğu dizinin yapılandırmasını sabit tutmuyoruz? Statik dosyalar static/ dizininde tutulur diye bir kural koysak, ayar işini tamamen web çatısına bıraksak? (Hemen hazır cevap: staticfiles Django'da eskiden üçüncü parti bir uygulamaydı, şimdi contrib modülü içinde olmasına rağmen geriye uyumluluk ilkesi nedeniyle durum böyle.)

Bunlar cevabı bilinen, tahmin edilebilen sorular ve hem bu tip soruların cevaplarını dikkate alarak, hem de Nodejs ve CoffeeScript'in kendine has özelliklerini, avantajlarını ve o dille yazılmış altyapıları, kitaplıkları araştırarak küçük bir başlangıç yapmak istedim. Zevkine de olsa, potansiyel bir proje olarak düşünmekte fayda var. Şimdi Django'da proje oluşturma, proje dizinine girme ve geliştirme sunucusunu çalıştırma işlemlerini gösterelim:

$ django-admin.py startproject hello_django
$ cd hello_django/
$ python manage.py runserver
Validating models...

0 errors found
Django version 1.3.1, using settings 'hello_django.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

CoffeeScript ile yazmaya başladığım mini web çatımızın adı, juju. Juju'da henüz MVT altyapısı tamamlanmış değil; ama en azından runserver dediğimiz zaman bir sayfayla karşılaşabiliyoruz:

$ juju startproject hello_juju
$ cd hello_juju/
$ juju runserver
Server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Sayfaların ekran görüntülerini de paylaşalım:

http://static.gokmengorgen.net/blog/django-juju-runserver.png

Evet, başlangıç sayfaları neredeyse birebir aynı. Altyapısal olarak farklılıkların en başında, Juju'da tüm işlemler juju komutuyla yapılıyor. Uygulamalar apps dizininde tutuluyor ve ihtiyaç duyulabilecek tüm dizinler en başta oluşturuluyor:

$ ls hello_django/
__init__.py    manage.py    settings.py    urls.py

$ ls hello_juju/
apps/    logs/    pids/    settings.coffee    static/    urls.coffee

Güzel bir başlangıç yapmış olduk. Bir sonraki yazımda, örnek bir uygulama oluşturmayı göstereceğim. Umarım bu eğlencenin devamı gelir.

Benzer yazı analizi 1

03 October 2011 Yaşar Arabacı

Benzer yazıları bulup, ziyaretçiye öneri göstermek zannettiğimden çok daha zor gibi görünüyor. Bir yandan düzgün bir algoritma oluşturmaya çalışırken, bir yandan da şu anda katettiğim yolu (her ne kadar çok olmasa da) aktarayım istedim.

Yazıların benzerliklerini hesaplamak için, Text similarity: an alternative way to search MEDLINE adlı makalede kosinüs katsayısı (Cosine Coefficient) formülünü gördüm (daha önce de başka bir yerde görmüştüm bu formülü, ama çıkartamıyorum şimdi :) ) ve denemeye karar verdim. Şimdilik, kelime ağırlıklarını formüle eklemeden bir deneme yaptım. Şu şekilde bir python dosyası ortaya çıktı:

# -*- coding:utf-8 -*-
from django.utils.html import strip_tags

import os
import sys
from math import sqrt
PROJE_DIZINI = os.path.abspath(os.path.dirname(__file__))
UST_DIZIN = os.path.abspath(PROJE_DIZINI + "/../")
os.environ["DJANGO_SETTINGS_MODULE"] = "similarity.settings"
sys.path.append(UST_DIZIN)
from blog.models import Post

# Son makaleden geriye sarıcaz!

sonMakale = Post.objects.latest("pub_date")

i = sonMakale.id
imla = [".",",","?","!","\"","\'",":",";"]
def kelimeleriAl(post_objesi):
    tumu = unicode(post_objesi.title) + unicode(post_objesi.abstract) + unicode(post_objesi.post)
    tumu = strip_tags(tumu)
    for karakter in imla:
        tumu.replace(karakter,"")
    kelimeler = tumu.split(" ")
    tekil = []
    for kelime in kelimeler:
        if kelime not in tekil:
            tekil.append(kelime)
    return tekil

benzerlikler = []
while i > 0:
    j = i-1
    birisi = Post.objects.get(pk=i)
    birisi = kelimeleriAl(birisi)
    while j > 0:
        tumKelimeler = birisi
        
        oburu = Post.objects.get(pk=j)
        oburu = kelimeleriAl(oburu)
        
        
        
        for kelime in oburu:
            if kelime not in tumKelimeler:
                tumKelimeler.append(kelime)
        
        # vektör oluştur!
        birisi_vektor = []
        oburu_vektor = []
        for kelime in tumKelimeler:
            birisi_vektor.append(kelime in birisi and 1 or 0)
            oburu_vektor.append(kelime in oburu and 1 or 0)
        
        toplam = float(0)
        # Formül karelerini almamızı istiyor
        # ancak, 0 ve 1 sayılarının kareleri almak çok mantıklı gelmedi

        for k in range(0,len(tumKelimeler)):
            toplam += birisi_vektor[k] * oburu_vektor[k]
        
        birisi_toplami = 0
        for sayi in birisi_vektor:
            birisi_toplami += sayi
        
        oburu_toplami = 0
        for sayi in oburu_vektor:
            oburu_toplami += sayi
        
        bolen = sqrt(birisi_toplami * oburu_toplami)
        benzerlikler.append((Post.objects.get(pk=i), Post.objects.get(pk=j), toplam/bolen))
        j -= 1
    i -= 1
print("\n".join([ "%s ve %s => %2f" % (a,b,c) for a,b,c in sorted(benzerlikler, key = lambda x: x[2])]))

Bahsettiğim yazıda geçen formülü birebir uygulamaya çalıştım. kelimeleriAl fonksiyonunda, bir makelede geçen tüm tekil kelimeleri döndürüyorum. Sonra döngünün içerisinde karşılaştırılan her iki makale için bir vektör (vektör denir değil mi ona?) oluşturuyorum.

Vektör oluşturma işleminde, tumKelimeler (içinde tüm tekil kelimeleri barındıran liste) içerisinde her kelime için, eğer o kelime makaleye dahilse, o makalenin vektörüne 1, değilse 0 ekliyorum. Daha sonra da, bahsettiğim linkde gösterilen formülü uyguluyorum. Birkaç sonuç örneği verirsek:

Django şablonlarında php ve 0'dan blog'a Django(1) => 0.951225
Django South göçünde "unique" alan hatası ve 5 Django İpucu => 0.923972
Django'da Abstract Modeller ve 0'dan Bloga Django(3) => 0.917118
Django'da Url Taşıma ve 0'dan Bloga Django(4) => 0.851807
.
.
.
Django ve Url Düzeltme ve Python range ve xrange => 0.161989
If..Else yada Try..Except, hangisi ne zaman kullanılmalı? ve Python range ve xrange => 0.158546
Django Modelleriyle Paket Yöneticisi ve Python range ve xrange => 0.155110

Doğrusunu söylemek gerekirse, bu algoritmadan aldığım sonuçlar çok tatmin edici olmadı. Bunun nedeninin de kelime ağırlıklarını algoritmaya dahil etmememe bağlıyorum.

Bu algoritmanın şu anda bir diğer büyük eksikliği ise, gereksiz kelimeleri (bağlaçlar gibi) de hesaplamaya dahil etmesi. Bir gereksiz kelimeler listesi oluşturup, bir de onları çıkararak denemek gerek diye düşünüyorum. Eğer bu konuda bir yol katedersem, yeni yazılarda bahsedeceğim.

İyi geliştirmeler.

If..Else yada Try..Except, hangisi ne zaman kullanılmalı?

02 October 2011 Yaşar Arabacı

Python öğrenen geliştiriciler, birkaç basit ders ardından try...except yapısıyla hata yakalama ve kurtarma yapmayı öğrenir. Bunu gerekli yerlerde kullanabilecek bilgi ve deneyim seviyesine kısa sürede erişebilir. Ancak, try..except yapısının daha egzotik kullanımları da mümkün. Try..except yapısıyla daha okunaklı ve temiz kod yazılabilir mi, yada ne zaman bu yapıyı kullanmaktan kaçınmalıdır sorusuna değinmek istedim. Birazdan okuyacaklarınız, benim bu konudaki kişisel görüşlerimdir. Kanıtlanmış veya toplumca kabul görmüş gerçekler olabilir veya olmayabilir.

Bu yazıyı yazmamın arkasındaki sebep Wikibooks'da gördüğüm bir makale'deki şu cümledir:

If you have a complicated piece of code to choose which of several courses of action to take, it can be useful to use exceptions to jump out of the code as soon as the decision can be made.

Kabaca bir çeviriyle diyor ki, eğer birçok seçimin yapılması gereken karmaşık bir kodunuz varsa, kararın verilebileceği bir aşamada, exception (tr: istisna, kuraldışılık) kullanarak koddan dışarı sıçramak yararlı olabilir. Basit bir örnekle açıklamak gerekirse:

def ifelseile(a=None):
    if a is not None:
        return a + 1
    else:
        return 0

def tryexceptile(a=None)
    try:
        return a+1
    except:
        return 0

Her ne kadar bu iki örnek çok basit örnekler olsa da, ikinci kodun çok daha okunaklı ve anlaşılır olduğunu farketmişsinizdir. Ayrıca, biraz önce bahsettiğim wikibooks sayfasında da benzer bir örneğini yaptığı gibi, getattr, hasattr gibi fonksiyonlar ve __getitem__,__setitem__, gibi metodlar, try..except blokları ile birlikte daha okunaklı ve programlama mantığını daha düzgün ifade eden kodlar olabilirler.

# If .. Else
if hasattr(a,b):
    k = getattr(a,b)
else:
    k = "öntanımlı_değer"

# Try .. Except
try:
    k = getattr(a,b)
except:
    k="öntanımli_deger"

# __getitem__ örneği

my_dictionary = dict()

if "deneme" in my_dictionary:
    a = my_dictionary["deneme"]
else:
    a = None

try:
    a = my_dictionary["deneme"]
except:
    a = None
    

Yani, bence, eğer karar verilmesi gereken konu bir exception oluşturuyorsa, çoğu zaman try .. except yapısı tercih edilmeli, ancak, bu bazen verimliliği etkileyebilir.

#####
# Bu örnekde,  if .. else bloğu tercih edilmeli!
#####

### IF-ELSE ###

if isinstance(a,dict):
    zorHesaplananSayi = cokKarmasikHesapYap()
    a["hebele"] = zorHesaplananSayi
else:
   oksuz_sayilar += 1

### TRY-EXCEPT ###

try:
    zorHesaplananSayi = cokKarmasikHesapYap()
    a["hebele"] = zorHesaplananSayi
except:
    oksuz_sayilar += 1

Bu örnekde, exception oluşturacak noktaya kadar çok uzun bir işlem yapılması gerektiği için, if .. else yapısıyla doğrudan bu kısmı atlamak daha mantıklı. Bu yüzden, try..except yapısını doğru şekilde kullanmak biraz da programcının maharetine kalıyor.

İyi geliştirmeler.

Django'da pdb ile debug

28 September 2011 Yaşar Arabacı
Django'da geliştirdiğimiz web uygulamasının hata temizlemesini isterseniz pdb (python debugger) ile de yapabilirsiniz. Bu yazıda kısaca bunun nasıl yapıldığından bahsedeceğiz.

Django'nun kendine ait bir debug aracı var, ama django ile python debugger kullanmak isteyenler için, django-pdb var. django-pdb sayesinde django uygulamalarımızı pdb ile debug edebiliriz.

django-pdb'nin kurulumu pip ile kolayca yapılabilir. "pip install django-pdb" komutu django-pdb'nin kurulumunu sizin için yapacaktır. *Nix kullananların kendi dağıtımlarına ait depoları kontrol etmelerinde de fayda var. Eğer depolarda bulabiliyorsanız, kendi paket yöneticinizle de kurabilirsiniz.

pip install django-pdb

Kurulumu tamamladıktan sonra django ayar dosyanızdaki, yüklü uygulamalara (INSTALLED_APPS) django_pdb'yi ekleyerek django'da geliştirdiğimiz siteye dahil ediyoruz.

INSTALLED_APPS = (
  'django_pdb',
)

Debugger'ın çalışması için birkaç farklı yöntem var, ama hepsi için settings modülündeki DEBUG değişkeninin, True'ya eşitlenmesi gerekiyor. Aksi halde çalışmayacaktır. settings.DEBUG'ın True olduğundan emin olduktan sonra, ek bir işlem yapmadan django'nun kendi geliştirme sunucusunu başlatabilirsiniz. GET metodunda pdb olan herhangi bir sayfa'yı açmaya çalıştığınızda pdb devreye girecektir. (ÖRN: www.ornek.com/?pdb)

DEBUG = True

Eğer geliştirme sunucunuzu --pdb anahtarı ile başlatırsanız, yüklediğiniz her view sayfasıyla birlikte pdb devreye girecektir.

manage.py runserver --pdb

django-pdb'nin python paket indeksi (pypi) sayfasına da buradan ulaşabilirsiniz.

Python range ve xrange

17 September 2011 Yaşar Arabacı

Python 2 ile python 3 arasında range() fonksiyonu farklılık gösteriyor. Python betiklerinde kullanılan bu fonksiyon, eğer doğru python yorumlayıcısında çalıştırılmazsa, istenildiğinden farklı davranabilir. Bu sorundan kurtulmak için, aşağıdaki yöntemi kullanıyorum.

Yöntemden bahsetmeden önce, sorun hakkında biraz bilgi vereceğim. Python 2 sürümünde, range ve xrange adıyla iki farklı fonksiyon var. range isimli fonksiyon, bir liste döndürüyor. xrange isimli fonksiyon ise bir "generator" (tr: üretici) fonksiyon. Bu iki fonksiyon arasındaki fark, hafıza kullanımında. xrange fonksiyonu her çağırıldığında yeni bir obje döndürdüğü için, daha az hafıza kullanılıyor.

range ve xrange arasındaki bu fark nedeniyle, programlarınızda xrange fonksiyonunu tercih edenlerdenseniz, kodlarınızı python 3 yorumlayıcı çalışıtırmayacaktır. Çünkü python 3 ile birlikte, xrange fonksiyonu kaldırıldı ve range fonksiyonu, python 2'deki xrange fonksiyonu gibi davranmaya başladı.

Aşağıda görülebilen örnek kod ile, python sürümleri arasındaki farkdan oluşan bu sorunun üstesinden gelebilirsiniz. Bu kodları modülünüzün yukarılarında kullanmalı, ve xrange kullanmak yerine range kullanmayı tercih etmelisiniz. Bu kodun çalıştığı platforma göre, xrange ve range fonksiyonu kendiliğinden kullanılacak.

from sys import version_info
if version_info[0] == 2:
    range = xrange

virtualenv nedir? Yenir mi?

17 August 2011 Cihan Okyay
İşimde ve kendi projelerimde genelde Django kullanıyorum. Django ile yazılım geliştirirken bir çok üçüncü parti uygulama kurmak gerekebiliyor. Örneğin migration işlemleri için south kurmak gerekiyor. Diyelim ubuntu kullanıyorsunuz ve south ubuntu deposunda var. Veya pip, easy_install gibi python paket sistemleri ile kurulum yapıyorsunuz. Bu durumda bu paketler sistemin geneline kuruluyor. Bu durumun ne gibi sıkıntıları [...]

Hello World!

17 August 2011 Cihan Okyay
#!/usr/bin/env python print ("hello world!")

Django Deployment Workshop

17 August 2011 Cihan Okyay
Merhabalar, Herhalde PyCon, Python ile ilgilenen yazılımcıların gitmek istedikleri etkinliklerin başında gelir. Tabi bu şansa herkes sahip olamıyor. Neyse ki PyCon’da ki çoğu sunu kayıt altına alınıyor ve bizde bundan yararlanıyoruz. Son günlerde iş arkadaşım Uğur Özyılmazel (vigo) ile işten kalan zamanlarımızda Jacob Kaplan Moss‘un Django deployment workshop sunumunun kaydını izledik. Maalesef PyCon 2010 sunumunu [...]

Django’da TinyMCE entegrasyonu

17 August 2011 Cihan Okyay
Merhabalar, TinyMCE, web uygulamamızda haber girişi, blog postu vb. girerken bir çok kolaylığı sağlayan bir araç. Örneğin, yazı stillerini belirlemek, paragrafları düzenlemek, yazıya resim veya video eklemek gibi işlevlerde yardımcı oluyor. Django’nun en iyi özelliklerinden birisi olan admin paneli içinde tinymce ile gelmiyor. Kolayca biz ekleyebiliriz. Bu adresten TinyMCE’yi indirdikten sonra jscripts altındaki tiny_mce dizinini [...]

[tips & tricks] Django admin kullanıcı parolasını sıfırlamak nasıl?

17 August 2011 Cihan Okyay
Merhaba, Django ile yazılım geliştiren ve daha çok yeni öğrenmekte olan arkadaşlara yararlı olabilecek tips & tricks serisi yapmayı düşünüyordum. Bu ilk yazı olduğundan not düşme ihtiyacı hissettim. Django’da gömülü gelen bir admin paneli mevcut. Eğer INSTALLED_APPS bölümünde ‘django.contrib.admin’ aktif durumdaysa syncdb işlemi sırasında sizden admin kullanıcı adı ve parolası istiyor. Sizde buna göre admin [...]

Django'da Fabric Kullanımı

01 August 2011 Gökmen Görgen

Bir Django projesinde statik dosyaları bir dizinde toplayıp, sunuma hazır hale getirmek için kaç satır komut giriyorsunuz? Bu tip durumlarla baş etmek için küçük bir betik yazıp, o betiği "python deploy.py collectstatic" gibi belirli argümanlarla çalıştırarak üstesinden gelmek, her projeyi güncellerken tekrar tekrar aynı komutları girmekten daha pratik olabiliyor.

Daha da pratiği, fabric kullanmak. Fabric, bir web projesini sunmak için gerekli komutları yazabileceğimiz, argümanları kolayca tanımlayabileceğimiz, gerektiğinde kendi bilgisayarımızdan uzaktaki sunucuya bağlanmasını sağlayabileceğimiz bir kitaplık ve konsol uygulamasıdır. Django belgelerinde, fabric ile uzaktaki sunucuya erişip statik dosyaları sunuma hazır hale getiren örnek bir fonksiyon bulunuyor:

from fabric.api import *

# Hosts to deploy onto
env.hosts = ['www1.example.com', 'www2.example.com']

# Where your project code lives on the server
env.project_root = '/home/www/myproject'

def deploy_static():
    with cd(env.project_root):
        run('./manage.py collectstatic -v0 --noinput')

Eğer statik dosyaları sıkıştırmak, coffeescript'i javascript koduna çevirmek gibi statik dosyalarınız ile ilgili daha başka işlemleriniz varsa, deploy_static() fonksiyonu altında belirtebilirsiniz. Daha sonra konsoldan proje dizinine girip şu komutu vermeniz yeterli olacak:

fab deploy_static

Sadece Django değil, sunucunuzda sunacağınız herhangi bir web çatısı, cms veya uygulamada fabric kullanmak, konsol araçları yazmakta kolaylık sağlayabilir. Hatta Fabric'i kullanacağınız projenin ana dilinin Python olmasına bile gerek yok. Yeter ki bir konsol aracına ihtiyacınız olduğu an, Fabric'e göz atın: http://docs.fabfile.org/

Dizinler ve Dosyalar

28 April 2011 Bahadır Kandemir
Dizinler ve dosyalarla çalışıyorsanız, muhtemelen os.path modülünün sağladığı metodlar işinize yarayacaktır. İşte sık kullandığım metodlardan bazıları: import os DIR = '/etc./conf.d' for filename in os.listdir(DIR): filepath = os.path.join(DIR, filename) print filepath os.path.join() metodu ile, işletim sisteminin ne olduğuna bakmaksızın, dosya/dizin yolu birleştirme işlemi yapabilirsiniz. Bu metod, dosya yolunu oluşturacak isimleri kontrol ederek hataları da önler. [...]

Dict

28 April 2011 Bahadır Kandemir
Bir dict nesnesi içinde listeler barındırıyorsanız (PiSi paketleri ile ilgili betiklerde genellikle bunu yapıyoruz, sanırım çok seviyoruz), dict içinde bulunan bir listeye ekleme yapmanız gerektiğinde önce o indisin dict içinde bulunup bulunmadığını kontrol etmeniz gerekebilir: if 'packages' not in d: d['packages'] = [] d['packages'].append('apache') dict.setdefault() metodu ile bu işi hızlı bir şekilde, ve “tek satırda” [...]

Python Uygulamaları

28 April 2011 Bahadır Kandemir
Python uygulamaları yazarkan kullandığım şablon aşağıda. Guido’nun günlüğünde gördüğüm bir kalıptan türettim, senelerdir bunu kullanıyorum. #!/usr/bin/python # -*- coding: utf-8 -*- """ Sample Application """ # Standard library import sys # Constants RET_SUCCESS, RET_FAIL = range(2) def main(): """ All the magic happen here. """ return RET_SUCCESS if __name__ == '__main__': sys.exit(main())

Merhaba %s, nasılsın?

28 April 2011 Bahadır Kandemir
Python ile program yazıyorsanız, karakter dizileriyle işlem yapmış ve operatörleri kullanmışsınızdır mutlaka. >>> kelime = "deneme" >>> print "kelime: %s" % kelime kelime: deneme Ve tahminen, metnin sağa ya da sola dayalı olması için “%s” yerine “%#s” operatöründen yararlanmışsınızdır. Sıfırdan büyük bir sayı kullandığınızda sağa, sıfırdan küçük bir sayı kullandığınızda sola dayalı oluyor: >>> kelime [...]

Lambda

28 April 2011 Bahadır Kandemir
Döngü içinde lambda ile yeni bir fonksiyon tanımlıyorsanız, bazen beklediğiniz davranışı sergilemeyebilir Python. Aşağıdaki kodda, verilen argümanın kuvvetlerini alan 10 fonksiyon oluşturuyorum ve bir sonraki döngüde oluşturduğum her fonksiyona 2 göndererek 2′nin 0′dan 9′a kadar olan kuvvetlerini hesaplatıyorum. items = [] for i in range(10): func = lambda x: x ** i items.append(func) for f [...]

Sıralama

28 April 2011 Bahadır Kandemir
Listeleri sıraladığınızda, öntanımlı karşılaştırma metodu karakterleri ASCII numarasına göre kıyasladığı için Türkçe karakter içeren kelimeler listenin sonunda birikir. Yerele göre, karakterlerin düzgün karşılaştırılması ve sıralamanın doğru olması için, list.sort metodunu karşılaştırma yaparken locale.strcoll() kullanmaya zorlayabilirsiniz: words = [u"Pardus", u"Pardüs", u"Özgür", u"Yazılım", u"Python"] import locale locale.setlocale(locale.LC_ALL, "") words.sort(cmp=locale.strcoll) for word in words: print word

Beklenmeyen Davranışlar ve Python Sihri

28 April 2011 Bahadır Kandemir
SQLAlchemy ile uğraşırken, veritabanından çekilecek nesne listesinin filter() metodu ile filtrelenebildiğini görünce şaşırmadım, ancak bir anlığına, dökümandaki örnek bende “nasıl ya?” etkisi bıraktı: query.filter(User.name == 'ed') query.filter(User.name != 'ed') User.name == ‘eq’ kodu, normal şartlar altında, True ya da False üreteceğinden, filter() metoduna gönderilen True/False değeri nasıl istediğim filtrelemeyi sağlayacaktı? Birkaç saniyelik afallamanın ardından, User.name [...]

Python Bilmek

28 April 2011 Bahadır Kandemir
İyi derecede Python bildiğini iddia eden bir geliştiriciden, mülakat sırasında faktöryel hesaplayan bir kod yazması istenmiş. Geliştirici de aşağıdaki kodu yazmış: def factorial(n): x = 1 while 1 < n: x *= n n -= 1 return x Mülakatı yapan, geliştiricinin bu problemi recursion kullanarak çözmemesi karşısında şaşırmış ve “Recursion’dan haberdar mısınız?” diye sormuş, ulaka [...]

Konfigürasyon Dosyası Değiştirme

28 April 2011 Bahadır Kandemir
Sitedeki ilk uzun kod, /etc/hosts dosyasını düzenlemek için yazdığım ancak herhangi bir projede kullanmadığım yerel DNS kütüğü güncelleme fonksiyonu. import os.path def update_address(address, domains=None, filename="/etc/hosts"): """ Updates an address record in /etc/hosts file. Arguments: address: IP address domains: List of domain names filename: Path to hosts file Returns True if address was found in /etc/hosts [...]

Metin Parçalama

28 April 2011 Bahadır Kandemir
Farklı projeler üzerinde çalışan birden fazla geliştirici, sadece birkaç gün arayla aynı problemle karşılaştı. Çift tırnak ile gruplanmış metinleri düzgün bir şekilde parçalamaları gerekiyor, str.split elbette işlerini görmüyordu: print 'kelime1 "kelime grubu 1" kelime2 "kelime grubu 2"'.split() $ python arg1.py ['kelime1', '"kelime', 'grubu', '1', 'kelime2', '"kelime', 'grubu', '2'] Probleme çözüm ararken, konsol uygulamalarına argümanları çift [...]