Altblümler

 
5. Veri Yapıları

Bu bölümde öğrendiğiniz bazı şeyler daha detaylı açıklanmakta ve bazı yeni konulara da değinilmekte.

 
5.1 Listeler Üzerine Daha Fazla Bilgi

Liste veri tipinin birkaç metodu daha var. İşte liste nesnelerinin bütün metodları:

append(x)
Listenin sonuna bir eleman ekler; a[len(a):] = [x] ifadesine denktir. .

extend(L)
Listeyi verilen listedeki tüm elemanarı ekleyerek genişletir; a[ len(a):] = L ifadesine denktir. .

insert(i, x)
Verilen konuma bir eleman sokar. İlk argüman elemanın yerleştirileceği indistir. a.insert(0, x) ifadesi x'i listenin başına sokar, ve a.insert(len(a), x) ifadesi a.append(x) ifadesine denktir.

remove(x)
Liste içinde değeri x olan ilk elemanı listeden siler. Böyle bir öğe yok ise bu hatadır.

pop([i])
Verilen konumdaki elemanı listeden siler ve bunu geri döndürür. Eğer bir indis belirtilmediyse, a.pop() listedeki son elemanı siler ve geri döndürür. (i etrafındaki kare parantezler bu parametreanin seçimlik olduğunu belirtir. Bu notasyonu Python belgelerinde sıkça görebilirsiniz.)

index(x)
Değeri x olan elemanın indisini geri döndürür. Böyle bir eleman yok ise bu hatadır.

count(x)
x'in listede kaç adet bulunduğunu bulur ve bu değeri geri döndürür.

sort()
Listenin elemanlarını sıralar - yerinde.

reverse()
Listenin sırasını tersine çevirir - yerinde.

Liste metodlarının çoğunu kullanan bir örnek:

>>> a = [66.6, 333, 333, 1, 1234.5]
>>> print a.count(333), a.count(66.6), a.count('x')
2 1 0
>>> a.insert(2, -1)
>>> a.append(333)
>>> a
[66.6, 333, -1, 333, 1, 1234.5, 333]
>>> a.index(333)
1
>>> a.remove(333)
>>> a
[66.6, -1, 333, 1, 1234.5, 333]
>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.6]
>>> a.sort()
>>> a
[-1, 1, 66.6, 333, 333, 1234.5]

 
5.1.1 Listelerin Yığın Olarak Kullanılması

Liste metodları listelerin kolayca yığın olarak kullanılmasını sağlarlar. Yığına son giren eleman ilk çıkar. Yığının üzerine eleman eklemek için append() ve en üstteki elemanı almak için indis belirtmeden pop() kullanılır. Örnek:

>>> yigin = [3, 4, 5]
>>> yigin.append(6)
>>> yigin.append(7)
>>> yigin
[3, 4, 5, 6, 7]
>>> yigin.pop()
7
>>> yigin
[3, 4, 5, 6]
>>> yigin.pop()
6
>>> yigin.pop()
5
>>> yigin
[3, 4]

 
5.1.2 Listelerin Kuyruk Olarak Kullanılması

Listeleri kuyruk olarak da kullanmak mümkün. Bir kuyrukta ilk eklenen eleman ilk alınan elemandır (ilk giren ilk çıkar). Kuyruğun sonuna bir eleman eklemek için append() kullanılır. Sıranın başından bir eleman almak için ise 0 indisi ile pop() kullanılır. Örnek:

>>> kuyruk = ["Ali", "Veli", "Deli"]
>>> kuyruk.append("Küpeli")           # Küpeli kuyrukta
>>> kuyruk.append("Aylin")            # Aylin kuyrukta
>>> kuyruk.pop(0)
'Ali'
>>> kuyruk.pop(0)
'Veli'
>>> kuyruk
['Deli', 'Küpeli', 'Aylin']

 
5.1.3 Fonksiyonel Programlama Araçları

Listelerle kullanıldığında çok faydalı olan yerşeik fonksiyonlar vardır: filter(), map(), ve reduce().

"filter(fonksiyon, sıra)" sıra içerisinden fonksiyon(eleman) ın doğru sonuç verdiği elemanların bulunduğu (mümkünse aynı türden) bir sıra geri döndürür. Örneğin, bazı asal sayıları hesaplamak için şöyle yapılabilir:

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]

"map(fonksiyon, sıra)" sıranın her elemanı için fonksiyon(sıra) çağırır ve geri döndürülen değerlerin oluşturduğu listeyi geri döndürür. Örneğin bazı sayıların küplerini hesaplamak için şu yol izlenebilir:

>>> def cube(x): return x*x*x
...
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

map(fonksiyon, sıra)] ifadesinde birden fazla sıra da kullanılabilir; ancak bu durumda fonksiyon sıra sayısı kadar argümana sahip olmalıdır. fonksiyon her sıranın uygun elemanını bir argüman olarak alır; ancak sıralardan biri kısa ise eksik elemanlar için fonksiyona None argümanı geçirilir. Eğer fonksiyon adı için de None kullanılırsa argümanlarını geri döndüren bir fonksiyon etkisi yaratılır.

Bu iki özel durumu birleştirerek "map(None, list1, list2)" ifadesi ile bir çift diziyi çiftlerden oluşan bir diziye çevirebiliriz. Örnek:

>>> sira = range(8)
>>> def kare(x): return x*x
...
>>> map(None, sira, map(kare, sira))
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36), (7, 49)]

"reduce(fonksiyon, sıra)" ifadesi tek bir değer geri döndürür. Bu değer şöyle elde edilir: iki argümanlı fonksiyona sıranin ilk iki elemanı argüman olarak verilir, sonra da elde edilen sonuç ile sıranin sonraki elemanı argüman olarak verilir,daha sonra yine elde edilen sonuç ile bir sonraki eleman fonksiyona verilir ve bu işlem bütün elemanlar için tekrarlanır. Örneğin 1'den 10'a kadar olan böyle toplanabilir:

>>> def topla(x,y): return x+y
...
>>> reduce(topla, range(1, 11))
55

Sırada sadece bir eleman var ise bunun değeri geri döndürülür; sıra boş ise bir istisna oluşur (exception).

Başlangıç değerini bildirmek için üçüncü bir argüman kullanılabilir. Bu durumda fonksiyona ilk olarak başlangıç değeri ve sıranın ilk elemanına uygulanır ve diğer elemanlar ile devam eder. Örnek:

>>> def sonuc(sira):
...     def topla(x,y): return x+y
...     return reduce(topla, sira, 0)
...
>>> sonuc(range(1, 11))
55
>>> sonuc([])
0

5.1.4 Liste Üreteçleri

Liste üreteçleri map(), filter() ve/veya lambda fonksiyonlarını kullanmadan liste yaratmanın kısa bir yoludur. Bu yolla yaratılan liste tanımı genellikle daha kolay anlaşılır olur. Bir liste üreteci bir ifade ve bir for döngüsü ile bunları izleyen sıfır ya da daha fazla for veya if ifadelerinden oluşur. Sonuç kendisini izleyen for ve if bağlamında değerlendirilen ifadeden oluşan bir listedir. Eğer ifade bir demete (değişmez liste [tuple]) dönüşecekse parantez içinde yazılmalıdır.

>>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
>>> [weapon.strip() for weapon in freshfruit] # elemanları saran
boşlukların atıldığı yeni bir liste
['banana', 'loganberry', 'passion fruit']
>>> vec = [2, 4, 6]
>>> [3*x for x in vec]
[6, 12, 18]
>>> [3*x for x in vec if x > 3]
[12, 18]
>>> [3*x for x in vec if x < 2]
[]
>>> [{x: x**2} for x in vec]   # sözlüklerden oluşan bir liste
[{2: 4}, {4: 16}, {6: 36}]
>>> [[x,x**2] for x in vec]
[[2, 4], [4, 16], [6, 36]]
>>> [x, x**2 for x in vec]   # hata - demet için parantez gerekir
  File "<stdin>", line 1, in ?
    [x, x**2 for x in vec]
               ^
SyntaxError: invalid syntax
>>> [(x, x**2) for x in vec]
[(2, 4), (4, 16), (6, 36)]
>>> vec1 = [2, 4, 6]
>>> vec2 = [4, 3, -9]
>>> [x*y for x in vec1 for y in vec2]
[8, 6, -18, 16, 12, -36, 24, 18, -54]
>>> [x+y for x in vec1 for y in vec2]
[6, 5, -7, 8, 7, -5, 10, 9, -3]
>>> [vec1[i]*vec2[i] for i in range(len(vec1))]
[8, 12, -54]

Liste üreteçlerinin for döngülerine benzer davranması için, döngü değişkenine yapılan atamalar üreteç dışında da görünürler:

>>> x = 100                     # bu değişecek
>>> [x**3 for x in range(5)]
[0, 1, 8, 27, 64]
>>> x
4                               # range(5) için son değer
>>

 
5.2 del Deyimi

del deyimi ile bir listeden indisi verilen bir eleman silinebilir. Bu deyim ile bir listeden dilimler de silinebilir (bunu daha önce dilimlere boş bir liste atayarak yapmıştık). Örnek:

>>> a
[-1, 1, 66.6, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.6, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.6, 1234.5]

del değişkeni tamamen silmek için de kullanılabilir:

>>> del a

Bu aşamadan sonra a ismine değinmek hatadır (aynı isme başka bir değer atanana kadar). Daha sonra del için başka kullanım alanları da göreceğiz.

 
5.3 Demetler ve Sıralar

Listelerin ve karakter dizilerinin indisleme ve dilimleme gibi pek çok ortak özellikleri olduğunu gördük. Bunlar sıra şeklindeki iki veri tipidirler. Python gelişmekte olan bir dil; diğer sıra şeklindeki veri tipleri de Python'a eklenebilir. demet de başka bir sıra şekilli standart veri tipidir .

Bir demet virgül ile ayrılmış birkaç değerden oluşur.

>>> t = 12345, 54321, 'merhaba!'
>>> t[0]
12345
>>> t
(12345, 54321, 'merhaba!')
>>> # demetler iç içe kullanılabilirler :
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'merhaba!'), (1, 2, 3, 4, 5))

Gördüğünüz gibi çıktıda demetler daima parantez içinde görünürler; ki iç içe geçmiş demetler belli olsun. Demetler parantezli veya parantezsiz olarak yazılabilirler; ancak parantezler genelikle gereklidirler (özellikle de demet daha büyük bir ifadenin içinde geçiyorsa).

Demetlerin pekçok kullanım alanı var: (x,y) koordinat çifti, veri tabanındaki işçi kayıtları vb. gibi. Demetler de karakter dizileri gibi değerleri değiştirilemez veri tipleridir; bunların elemanlarına atama yapılamaz (fakat dilimleme ve birleştirme aracılığı ile bu etki sağlanabilir). Ayrıca değiştirilebilen elemanlardan oluşan demetler oluşturmak da mümkündür (örnek: listelerden oluşan bir demet).

Sıfır veya bir elemanlı demetlerin oluşturulması ile ilgili özel bir problem var: bunların ifade edilmesini sağlayan sözdizim biraz acayip. Boş demetler bir çift boş parantez ile ifade edilir. Tek elemanı olan bir demet için ise elemandan sonra bir virgül kullanılır (tek bir değeri parantez içine almak yeterli değildir). Çirkin ama etkili. Örnek:

>>> bos = ()
>>> tekOge = 'merhaba', # <--satır sonundaki virgüle dikkat
>>> len(bos)
0
>>> len(tekOge)
1
>>> tekOge
('merhaba',)

t = 12345, 54321, 'merhaba!' ifadesi demetleme (tuple packing) işlemine bir örnektir: 12345, 54321 ve 'merhaba!' değerleri bir demet içinde toplanmışlardır. Bu işlemin tersi de mümkün:

>>> x, y, z = t

Doğal olarak, buna demet açma(sequence unpacking) deniyor . Demet açma sol taraftaki değişken sayısının sıra içindeki öğe sayısına eşit olmasını gerektirir. Çoklu değer atama işleminin aslında demetleme ve demet açmanın bir bileşimi olduğuna dikkat edin.

Burada küçük bir asimetri var: birden fazla değeri demetleme her zaman bir demet oluşturur ve demet açma herhangi bir sıra için yapılabilir. Örnek:

>>> paket = 'xyz' # bir karakter dizisi
>>> a,b,c = paket
>>> a
'x'
>>> b
'y'
>>> c
'z'

 
5.4 Sözlükler

Python'da bulunan bir diğer faydalı veri tipi de sözlüktür. Sözlükler diğer programlama dillerinde ``çağrışımlı bellek'' ( associative memory) veya ``çağrışımlı dizi'' (associative array) olarak bilinirler. Sayılarla indislenen sıralardan farklı olarak, sözlükler anahtarlar (key) ile indislenirler. Anahtar değiştirilemeyen tipdeki herhangi bir veri tipinde olabilir. Sayılar ve karakter dizileri her zaman anahtar olabilirler. Demetler de sayılar, karakter dizileri veya demetler içerdikleri sürece anahtar olabilirler. Bir demet doğrudan ya da dolaylı olarak değiştirilebilir bir nesne içeriyorsa anahtar olarak kullanılamaz. Listeler anahtar olamazlar, çünkü append() ile extend() metodları, dilimleme ve indise değer atama ile değiştirilebilirler.

Bir sözlük anahtar : değer çiftlerinden oluşur. Bir anahtar sözlükte sadece bir defa bulunabilir. Bir çift çengelli parantez boş bir sözlük yaratır : {}. Çengelli parantezlerin içine virgülle ayrılmış anahtar : değer çiftleri koymak anahtar ve değer çiftlerine ilk değerlerini verir. Çıktıya da sözlükler aynı şekilde yazılırlar.

Sözlüklerle ilgili ana işlemler bir değerin bir anahtar ile saklanması ve anahtar verildiğinde değerin bulunmasıdır. del kullanarak bir anahtar : değer çiftini silmek mümkündür. Zaten mevcut olan bir anahtar kullanarak bir değer eklerseniz bu anahtarla bağlantılı eski değer unutulur. Mevcut olmayan bir anahtar ile değer istemek hatalıdır.

Sözlük nesnesinin keys() metodu listedeki bütün anahtarların listesini rasgele sıralı olarak geri döndürür (sıralamak isterseniz listenin sort() metodundan faydalanabilirsiniz) . Bir anahtarın sözlükte olup olmadığını görmek için sözlüğün has_key() metodu kullanılır.

İşte sözlük kullanan küçük bir örnek:

>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}
>>> tel.keys()
['guido', 'irv', 'jack']
>>> tel.has_key('guido')
1

dict() fonksiyonu anahtar-değer çiftlerinden oluşan demetlerden sözlükler üretir. Çiftlerin bir kalıba uyduğu durumlarda, liste üreteçleri ile anahtar-değer çiftleri kısaca ifade edilebilir.

>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'jack': 4098, 'guido': 4127}
>>> dict([(x, x**2) for x in vec])     # liste üreteci kullanarak
{2: 4, 4: 16, 6: 36}

 
5.5 Döngü Teknikleri

Sözlükler üzerinde döngüler kurarken o anki değer items() metodu ile aynı anda elde edilebilir.

>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
...     print k, v
...
gallahad the pure
robin the brave

Bir sıra üzerinde dönerken konum indisi ve ona karşılık gelen değer de enumerate() fonksiyonunu kullanarak aynı anda elde edilebilir.

>>> for i, v in enumerate(['tic', 'tac', 'toe']):
...     print i, v
...
0 tic
1 tac
2 toe

Aynı anda iki sıra üzerinde ilerlemek için ise zip() fonksiyonu ile bunlar çiftler haline getirilebilir.

>>> sorular = ['adın', 'görevin', 'favori rengin']
>>> cevaplar = ['Adnan', 'Uyumak', 'Mavi']
>>> for s, c in zip(sorular, cevaplar):
...     print 'Senin %s ne? %s.' % (s, c)
...
Senin adın ne? Adnan.
Senin görevin ne? Uyumak.
Senin favori rengin ne? Mavi.

 
5.6 Koşullar Üzerine Daha Fazla Bilgi

while ve if deyimlerinde kıyaslama dışında da işleçler kullanılabilir.

in ve not in kıyaslama işleçleri bir değerin bir sıra içinde olup olmadığını sınarlar. is ve is not isleçleri iki nesnenin tamamen aynı nesne olup olmadıklarını sınarlar (bu sadece liste gibi değiştirilebilir nesnelerde önemlidir). Bütün kıyaslama isleçleri aynı önceliğe sahiptirler ve bu sayısal isleçlerinkinden düşüktür.

Kıyaslamalar zincirlenebilir: a < b == c gibi.

Kıyaslamalar mantıksal isleçler and ve or ile ile birleştirilebilirler, ve kıyaslamanın sonucu (ya da herhangi bir mantıksal ifade) not ile değillenebilirler. Bunların hepsi de kıyaslama isleçlerinden düşük önceliğe sahiptirler ve aralarında en yüksek öncelikli olan notve en düşük öncelikli olan or islecidir. Örneğin A and not B or C ifadesi (A and (not B)) or C ifadesine eştir. İstenen bileşimi elde etmek için parantezler kullanılabilir.

and ve or mantıksal isleçlerine kısa devre isleç de denir. Bunların argümanları soldan sağa değerlendirilir ve sonuç belli olur olmaz değerlendirme işlemi kesilir. Örneğin A ve C doğru, fakat B yanlış olsun. A and B and C ifadesinde C ifadesi değerlendirilmez (çünkü C nin değeri sonucu değiştirmez). Genel olarak bir kısa devre isleci Bool değil de genel bir değer gibi kullanıldığında en son değerlendirilen argümanın değeri geri döndürülür.

Bir kıyaslamanın ya da mantıksal ifadenin sonucunu bir değişkene atamak mümkündür:

>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'

C dilinin tersine, Python'da ifadelerin içinde atama olamayacağına dikkat edin. C programcıları bundan şikayetçi olabilirler; ancak bu C programlarında sık karşılaşılan bazı hataları engellemektedir (== yerine = yazmak gibi).

 
5.7 Sıraların ve Diğer Veri Tiplerinin Kıyaslanması

Sıra nesneleri yine sıra şeklindeki diğer nesnelerle kıyaslanabilirler. Önce ilk iki eleman kıyaslanır. Bunlar farklı ise sonuç belli olmuştur; eşit olmaları halinde sonraki iki eleman kıyaslanır ve sıralardan biri tükenene kadar bu işlem tekrarlanır. Eğer kıyaslanan iki öğe de sıra ise bunlar da kendi aralarında kıyaslanırlar. İki sıranın bütün öğeleri aynı bulunursa bu sıralar eşit kabul edilir. Eğer bir sıra diğerinin başından bir kısmı ile aynı ise kısa olan sıra küçük kabul edilir. Karakterlerin kıyaslanmasında ASCII karakter sırası kullanılır. Aynı türden sıraların kıyaslanmasına bazı örnekler :

(1, 2, 3)              < (1, 2, 4)
[1, 2, 3]              < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4)           < (1, 2, 4)
(1, 2)                 < (1, 2, -1)
(1, 2, 3)             == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

Farklı türden nesnelerin kıyaslanmasının yasal olduğuna dikkat edin. Türler alfabetik sırayla dizilmiştir (ingilizce isimlerine göre). Yani: liste < karakter dizisi < demet (list < string < tuple). 5.1



Footnotes

... tuple).5.1
Değişik türlerin kıyaslanmasına ilişkin kurallara güvenilmemeli; gelecek Python sürümlerinde bu kurallar değişebilir !