Sözlük, Dictionary Veri Yapısı - 1
8108
Sözlükler yani dictionaries anahtar-değer çiftlerini tutan ve anahtar değeri verildiği zaman hızlı bir biçimde değer’in bulunmasını sağlayan veri yapılarıdır. Bunun olabilmesi için arka planda bu doğrultuda organize edilmiş veri yapıları ve algoritmalar bulunur. Burada genelde hash tables, balanced binary tree, sorted arrays gibi veri yapıları kullanılır. CPython ise kümelerde olduğu gibi sözlüklerde de hash tables tercih eder.
Sözlüklerin Yaratılması
Sözlükler kümeler gibi yaratılır, {} kullanarak. Fakat bu sefer
anahtar:değer şeklinde bir formda veririz. Şöyle ki:
{anahtar:değer, anahtar:değer, anahtar:değer}
d = {'ankara': 6, 'istanbul': 34, 'eskişehir': 26, 'izmir': 35, 'denizli': 20}
Sözlükler dict isimli sınıfla temsil edilir.
>>> x = {'alper':'yazar'}
>>> type(x)
<class 'dict'>
>>> x
{'alper': 'yazar'}
Anahtarlar
Sözlüklerde anahtarların hashlenebilir olması gerekmektedir. Değerler için ise
böyle bir koşul yoktur. Bu yüzden anahtarlar int, float, str gibi
türlerden olabilir ama list ve set türünden olamaz. tuple ve frozenset
ise eğer elemanları hash’lenebilir ise hash’lenebilir olmaktadır.
>>> d = {[10, 20]: 100}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> d = {(10, 20): 100}
>>> d
{(10, 20): 100}
Anahtar ve değerlerin aslında aynı sözlük içerisinde aynı türden olmasına gerek yoktur. Ama pratikte genelde aynı türden olmaktadır.
d = {'ali': 100, 200: 'veli', 20: 30.2} # geçerli
Hashlenebilirlik
Sözlükler hashlenebilir değildir. O yüzden bir sözlük anahtar olamaz. Ama değerler sözlük olabilir.
>>> d = {'içanadolu': {'eskişehir': 26, 'konya': 42, 'ankara': 6},
'ege': {'izmir': 35, 'aydın': 9},
'marmara': {'istanbul': 34, 'kocaeli': 41}} # geçerli
Değişkenler ve Sabitler
Liste, demet, küme ve sözlüklerin elemanları sabit olmak zorunda değildir, bunu hatırlatma olarak ekleyelim. Elemanlar değişken olarak da verilebilir. Zaten biz bir sabit oluşturduğumuzda o sabit için önce bir nesne yaratılmakta sonra o nesnenin adresi kullanılmaktadır. Bunu doğrudan yapmak ile dolaylı yapmak arasında bir fark yoktur.
x = 26
d = {x: 'eskişehir'}
# ya da
d = {26: 'eskişehir'}
Hatırlarsan atama işlemini kolaylık olsun diye operatör gibi ele almıştık ama
aslında atama bir deyimdir. O yüzden a = [x = 10 + 20, 20] ifadesi geçerli
değildir. Ama burada Walrus operatörünü kullanabiliriz.
>>> a = [x = 10 + 20, 20]
File "<stdin>", line 1
a = [x = 10 + 20, 20]
^
SyntaxError: invalid syntax
>>> a = [x := 10 + 20, 20]
>>> a
[30, 20]
>>> x
30
dict Sınıfı
Sözlükler dict sınıfı ile temsil edilir. Diğer veri yapılarında olduğu gibi
dict() fonksiyonu ile sözlük nesnesi yaratabiliriz, d = dict() gibi.
Kümeler kısmında da konuştuğumuz gibi boş {} bir boş küme değil boş sözlük
yaratmaktadır. Boş küme için set() kullanmalıyız demiştik.
dict() Fonksiyonu
Eğer dict() fonksiyonuna biz iki elemanlı dolaşılabilir nesnelerde oluşan bir
dolaşılabilir bir nesneyi argüman olarak verirsek, dict() fonksiyonu bu
nesneyi dolaşır. Her dolaşımda iki elemanlı bir dolaşılabilir nesne elde eder. O
iki elemanlı nesneyi de dolaşarak ilk elemanı anahtar, ikinci elemanı değer
olarak bir sözlük nesnesi oluşturur.
>>> a = [('ali', 10), ('veli', 20), ('selami', 30), ('ayşe', 40), ('fatma', 50)]
>>> d = dict(a)
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
Örneğin burada a bir list yani dolaşılabilir bir nesne. Her elemanı da bir
tuple ve o da bir dolaşılabilir nesne. Sonuçta görünen değerler bir sözlük
oluşur.
Ama farklı şeyler de yapabiliriz tabii ki
>>> a = (['selami', 30], ('ayşe', 40), ['fatma', 50], 'ak', 'tk', 'xy', range(2))
>>> d = dict(a)
>>> d
{'selami': 30, 'ayşe': 40, 'fatma': 50, 'a': 'k', 't': 'k', 'x': 'y', 0: 1}
Eğer dolaşılabilir nesnenin elemanları, iki elemandan daha fazla eleman içeren
dolaşılabilir nesneler ise bu durumda ValueError exception oluşur.
>>> d = dict([('ali', 10), ('veli', 20, 30)])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: dictionary update sequence element #1 has length 3; 2 is required
8321
Aşağıdaki örnekler gibi farklı şekilde de dictionary oluşturabiliriz. Yeter ki dolaşılabilir nesneler olsun.
>>> a = [('ali', 10), ('veli', 20), ('selami', 30), ('ayşe', 40), ('fatma', 50)]>>> d = dict(a)
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> a = [['ali', 10], ['veli', 20], ['selami', 30], ['ayşe', 40], ['fatma', 50]]>>> d = dict(a)
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
dict() fonksiyonuna değişken=değer biçiminde argümanlar girersek fonksiyon
bize o değişkenin string halini anahtar, = operatörünün sağındakileri de
değer yaparak bize bir sözlük nesnesi oluşturur.
Not
Demek ki içeride bir yerde stringify yapabiliyoruz.
Örnek:
>>> d = dict(x=10, y=20, z=30)
>>> d
{'x': 10, 'y': 20, 'z': 30}
Bu biçimde sözlük nesnesi oluşturma işlemi pek yaygın değildir. Burada değişken ismi yerine başka bir şey getirilemez.
>>> d = dict('ali'= 10, 'veli'=20, 'selami'=30)
File "<stdin>", line 1
d = dict('ali'= 10, 'veli'=20, 'selami'=30)
^
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
>>> d = dict(10='ali', 20='veli', 30='selami')
File "<stdin>", line 1
d = dict(10='ali', 20='veli', 30='selami')
^
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
Her ikisinde de hata aldık.
Sözlük Nesnesinden Sözlük Nesnesi Oluşturma
dict() fonksiyonunu bu amaçla da kullanabiliriz.
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> k = dict(d)
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> k
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> id(d)
548591851648
>>> id(k)
548591852736
Değerlerin Elde Edilmesi
[] operatörü ile anahtar verip o anahtara karşılık gelen değeri elde
edebiliriz. Eğer ilgili anahtar sözlükte yoksa KeyError exception oluşur.
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> d['ayşe']
40
>>> d['fatma']
50
>>> d['alper']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'alper'
Elbette sözlüklerde anahtar ve değerlerin türleri aynı olmak zorunda değildir.
>>> d = {'ali': 10, 20: 'veli', 1.5: 'selami'}
>>> d['ali']
10
>>> d[20]
'veli'
get() Metodu
dict sınıfının get() isimli metodu da anahtar değerden değer bulmak için
kullanılır. Fakat ilgili anahtar yoksa bu sefer exception oluşmaz. Eğer ikinci
parametre geçersek, bu opsiyoneldir, bulunamadığı durumda bize bu değeri geri
döner.
Örneğin:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> d.get('ayşe')
40
>>> d.get('sacit')
>>> d.get('ayşe', 'bulunamadı')
40
>>> d.get('sacit', 'bulunamadı')
'bulunamadı'
>>> d.get('sacit', 3.14)
3.14
Önemli
Sözlüklerde değer üzerinden anahtar değer araması yapılamaz. Yani değer verilerek anahtar elde edilemez.
in ve not in Operatörleri
Belli bir anahtarın sözlükte olup olmadığını anlamak için kullanılır. Kümelerde olduğu gibi burada da bu operatörler hızlı çalışır. Buradan değer elde edemeyiz.
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> 'ayşe' in d
True
>>> 20 in d
False
>>> 20 not in d
True
len() Fonksiyonu
8495
Python’daki built-in len() fonksiyonu ile sözlük nesnesinin içerisinde kaç
tane anahtar-değer çifti olduğunu öğrenebiliriz.
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> len(d)
5
>>> d = {}
>>> len(d)
0
Sözlükler Dolaşılabilir Nesnelerdir
Sözlükler de iterable yani dolaşılabilir nesnelerdir. Bir sözlük nesnesi dolaşıldığında yalnızca anahtar değerler elde edilir.
Önemli
Python 3.6’ya kadar (sanıyorum dahil) bu dolaşım sırasında anahtarlar rastgele sırada elde edilebiliyordu. Fakat sonrasında sözlük nesneleri dolaşıldığı zaman anahtarların eklenme sırası ile elde edileceği garanti edildi.
Örnek:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> list(d)
['ali', 'veli', 'selami', 'ayşe', 'fatma']
Bu örnekte list() fonksiyonu dolaşılabilir bir nesne aldı fakat sadece
anahtar değerlerden bir liste elde etmiş olduk.
Not
dict() fonksiyonunu işlerken elemanlarının da dolaşılabilir olması
gerektiğinden bahsettik. Fakat şimdi sözlüklerin dolaşılınca sadece
anahtarlarının elde edildiğini söyledik. Oysa ki dict() ile bir sözlükten
başka sözlük elde edebiliyorduk. Yani dict()e sözlük verince farklı bir
durum mu oluşuyor?
Mesela Python dokümanlarında şöyle verilmiş:
class dict(**kwargs)
class dict(mapping, **kwargs)
class dict(iterable, **kwargs)
Belki henüz anlayamadığım bir şeyler var?
Sözlükler Değiştirilebilir, Mutable, Türlerdir
8536
Bir sözlüğe yeni bir anahtar-değer çifti ekleyebiliriz ya da silebiliriz. Mevcut bir anahtarın karşılığı olan değeri değiştirebiliriz.
Bir sözlükte anahtarlar unique yani tekil konumdadır. Bir anahtardan birden fazla olamaz. Yeni bir anahtar-değer çiftini aynı değer ile eklersek önceki değer değiştirilmiş olur.
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ali': 40, 'selami': 50}
>>> d
{'ali': 40, 'veli': 20, 'selami': 50}
Burada ali ve selami den iki adet eklememize rağmen teke düştüler.
Sözlüğe yeni bir anahtar-değer çifti eklemenin en kolay yolu d[key] = value
şeklinde bir atama yapmaktır. Var olan bir key ise değeri değişir, yoksa
eklenir.
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> d['sacit'] = 60
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50, 'sacit': 60}
>>> d['sacit'] = 100
>>> d
{'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50, 'sacit': 100}
Birden Fazla Anahtar-Değer Ekleme
Bunun için update() metodunu kullanabiliriz. Bu metod, dict() fonksiyonunun
kabul ettiği tarzda argümanlar almaktadır. Yani dolaşılabilir bir nesne
verilmeli ve dolaşım sonucunda elde edilen nesnelerin kendileri de iki elemanlı
dolaşılabilir nesneler olmalıdır. Yine dict() metodunda olduğu gibi ilk
nesne anahtar, ikincisi ise değer olmaktadır. Eğer anahtar halihazırda var ise
ekleme yapılmaz, güncelleme yapılır.
>>> d = {'ali': 10 }
>>> d
{'ali': 10}
>>> d.update([('sacit', 100), ('mehmet', 200), ('sibel', 300)])
>>> d
{'ali': 10, 'sacit': 100, 'mehmet': 200, 'sibel': 300}
Sözlükten Silme Yapılması
Bunun için pop() isimli metod kullanılabilir. Bizden anahtar alır ve ilgili
çifti siler. Silinen değeri de geri dönüş değeri olarak bize verir.
Eğer pop()u tek argüman ile çağırmışsak ve ilgili anahtarı bulamazsa
KeyError exception oluşur. İki argümanla çağırırsak ise böyle bir durumda
exception oluşmaz, bize ikinci argümanı verir.
Örnek:
>>> d = {'ali': 10, 'veli': 20, 'selami': 30, 'ayşe': 40, 'fatma': 50}
>>> d.pop('selami')
30
>>> d
{'ali': 10, 'veli': 20, 'ayşe': 40, 'fatma': 50}
>>> d.pop('sibel')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'sibel'
>>> d.pop('sibel', 'anahtar yok')
'anahtar yok'
8647