Python'da küme (set), aynı elemanı iki kez içermeyen, sırasız ve değiştirilebilir bir koleksiyondur: {'elma', 'armut'}. Kümeler en çok üç iş için kullanılır: bir değerin koleksiyonda olup olmadığını hızlıca sınamak, bir dizideki tekrarları atmak ve matematikteki birleşim (|), kesişim (&), fark (-) ve simetrik fark (^) işlemlerini yapmak. Değiştirilemeyen kardeş tip frozenset ise hash edilebilir olduğu için sözlük anahtarı ya da başka bir kümenin elemanı olabilir.

Bu yazıda küme oluşturmanın kurallarını (boş kümenin neden {} olmadığını da), eleman ekleme ve silme metotlarını, küme işlemlerinin metot ve işleç biçimleri arasındaki ince farkı, alt küme sorgularını, frozenset'i ve kümelerin temelindeki hash edilebilirlik kuralını Python 3.15.2 ile çalıştırılmış örneklerle anlatıyoruz. Genel öğrenme sırası için Python öğrenme rehberi sayfasına bakabilirsiniz.

Küme oluşturmak ve boş küme tuzağı

Boş olmayan bir küme süslü parantez içinde virgülle ayrılmış elemanlarla yazılır. set() yapıcısı herhangi bir yinelenebilirden küme kurar; bir string verirseniz harflerinden oluşan bir küme elde edersiniz. Yinelenen elemanlar otomatik olarak tek bir elemana iner. Belgelerin özellikle uyardığı nokta şudur: boş küme için mutlaka set() yazmalısınız, çünkü {} boş bir sözlük oluşturur. Python'da boş küme için ayrı bir sabit söz dizimi yoktur. Kümeleri set comprehension ile de kurabilirsiniz.

python
sepet = {'elma', 'portakal', 'elma', 'armut', 'portakal', 'muz'}
print(len(sepet))                 # çıktı: 4
print(sorted(sepet))              # çıktı: ['armut', 'elma', 'muz', 'portakal']
print('portakal' in sepet)        # çıktı: True
print('ayva' in sepet)            # çıktı: False

bos = set()
print(type({}), type(bos))        # çıktı: <class 'dict'> <class 'set'>
print(bos)                        # çıktı: set()
print(sorted(set('abracadabra'))) # çıktı: ['a', 'b', 'c', 'd', 'r']
print(sorted({h for h in 'abracadabra' if h not in 'abc'}))   # çıktı: ['d', 'r']

Kümeler sırasızdır: yazdırma sırasına güvenmeyin

Kümeler elemanların konumunu ya da eklenme sırasını kaydetmez. Bu yüzden indeksleme (s[0]), dilimleme ve diğer dizi davranışları kümelerde yoktur. Belgeler ayrıca bir kümeyi dolaşırken ya da yazdırırken elemanların beklediğinizden farklı bir sırada gelebileceğini belirtir. Bunun pratik bir nedeni de var: veri modeli belgelerine göre string ve bytes nesnelerinin hash değerleri her Python sürecinde öngörülemeyen rastgele bir değerle "tuzlanır" ve bu durum kümelerin dolaşılma sırasını etkiler. Aynı programı iki kez çalıştırdığınızda string içeren bir kümenin farklı sırada yazdırılması normaldir. Bu yazıdaki örneklerde çıktıyı sabit tutmak için kümeleri sorted() ile listeye çevirerek yazdırıyoruz.

python
s = {'mavi', 'kırmızı'}
try:
    s[0]
except TypeError as hata:
    print(hata)    # çıktı: 'set' object is not subscriptable

Eleman eklemek ve silmek

add(x) tek bir eleman ekler; eleman zaten varsa hiçbir şey olmaz. update() bir ya da daha fazla yinelenebilirin tüm elemanlarını ekler. Silmek için iki metot vardır ve aralarındaki fark önemlidir: remove(x) eleman yoksa KeyError fırlatır, discard(x) ise sessizce geçer. pop() kümeden rastgele bir elemanı çıkarıp döndürür (hangisi olduğu belirli değildir) ve boş kümede KeyError verir. clear() tüm elemanları siler.

python
etiket = {'python'}
etiket.add('linux')
etiket.add('python')                      # zaten var, etkisiz
etiket.update(['docker', 'git'], ('nginx',))
print(sorted(etiket))   # çıktı: ['docker', 'git', 'linux', 'nginx', 'python']

etiket.remove('git')
etiket.discard('yok')                     # hata vermez
try:
    etiket.remove('yok')
except KeyError as hata:
    print(repr(hata))   # çıktı: KeyError('yok')

cikan = etiket.pop()
print(len(etiket), cikan not in etiket)   # çıktı: 3 True

etiket.clear()
print(etiket)           # çıktı: set()
try:
    etiket.pop()
except KeyError as hata:
    print(hata)         # çıktı: 'pop from an empty set'

Birleşim, kesişim, fark ve simetrik fark

Birleşim, kesişim, fark ve simetrik fark: Metot mu işleç mi?, Yerinde güncelleyen işlemler

Kümelerin asıl gücü matematik işlemlerindedir. Her işlemin bir işleç ve bir metot biçimi vardır ve hepsi yeni bir küme döndürür, kaynak kümeleri değiştirmez. Belgelerdeki klasik örnekte iki kelimenin harfleri karşılaştırılır:

python
a = set('abracadabra')
b = set('alacazam')
print(sorted(a))       # çıktı: ['a', 'b', 'c', 'd', 'r']
print(sorted(a - b))   # çıktı: ['b', 'd', 'r']
print(sorted(a | b))   # çıktı: ['a', 'b', 'c', 'd', 'l', 'm', 'r', 'z']
print(sorted(a & b))   # çıktı: ['a', 'c']
print(sorted(a ^ b))   # çıktı: ['b', 'd', 'l', 'm', 'r', 'z']
İşlemİşleçMetotSonuç
Birleşima | ba.union(b, ...)İki kümedeki tüm elemanlar
Kesişima & ba.intersection(b, ...)İkisinde de olan elemanlar
Farka - ba.difference(b, ...)a'da olup b'de olmayanlar
Simetrik farka ^ ba.symmetric_difference(b)Yalnızca birinde olanlar
Alt kümea <= ba.issubset(b)a'nın her elemanı b'de mi
Öz alt kümea < b-a <= b ve a != b
Üst kümea >= ba.issuperset(b)b'nin her elemanı a'da mı
Ayrık mı-a.isdisjoint(b)Ortak eleman yok mu

Metot mu işleç mi?

Belgelerdeki önemli bir ayrıntı: union(), intersection(), difference(), symmetric_difference(), issubset() ve issuperset() metotları argüman olarak herhangi bir yinelenebilir kabul eder. İşleç biçimleri ise iki tarafın da küme olmasını ister. Belgeler bunun bilinçli olduğunu, set('abc') & 'cbs' gibi hataya açık yazımların önüne geçip daha okunaklı set('abc').intersection('cbs') biçimini teşvik ettiğini söyler. Ayrıca metot biçimlerinin çoğu birden fazla argüman alabilir; a.union(b, c, d) tek çağrıda dört kümeyi birleştirir.

python
s = set('abc')
print(sorted(s.intersection('cbs')))     # çıktı: ['b', 'c']
try:
    s & 'cbs'
except TypeError as hata:
    print(hata)   # çıktı: unsupported operand type(s) for &: 'set' and 'str'

sayilar = {1, 2, 3}
print(sorted(sayilar.union([4], (5,), range(6, 8))))   # çıktı: [1, 2, 3, 4, 5, 6, 7]

Yerinde güncelleyen işlemler

Her işlemin kümeyi yerinde değiştiren bir karşılığı da vardır: |= (update()), &= (intersection_update()), -= (difference_update()) ve ^= (symmetric_difference_update()). Bunlar yalnızca set için geçerlidir, frozenset değiştirilemediği için bu metotlara sahip değildir. Metot biçimleri yine her yinelenebilir ile çalışır.

python
s = {1, 2, 3, 4}
s |= {5}
s &= {1, 2, 5, 9}
s -= {1}
print(sorted(s))    # çıktı: [2, 5]
s ^= {5, 6}
print(sorted(s))    # çıktı: [2, 6]
s.difference_update([2])
print(s)            # çıktı: {6}

Alt küme, üst küme ve kısmi sıralama

Alt küme, üst küme ve kısmi sıralama

<=, <, >= ve > kümelerde büyüklük değil kapsama ilişkisi sorar. İki küme, her biri diğerinin alt kümesiyse eşittir. Belgeler bu karşılaştırmaların tam bir sıralama oluşturmadığını vurgular: ortak elemanı olmayan iki boş olmayan küme ne eşittir ne de birbirinin alt kümesidir, bu yüzden a < b, a == b ve a > b üçü birden False olur. Aynı sebeple kümelerden oluşan bir listeyi sort() ile sıralamanın sonucu tanımsızdır.

python
yetkiler = {'oku', 'yaz', 'sil'}
editor = {'oku', 'yaz'}
print(editor <= yetkiler, editor.issubset(yetkiler))   # çıktı: True True
print(editor < yetkiler)                 # çıktı: True
print(yetkiler >= editor)                # çıktı: True
print(yetkiler < yetkiler, yetkiler <= yetkiler)   # çıktı: False True
print({'oku'}.isdisjoint({'sil'}))       # çıktı: True

x, y = {1, 2}, {3, 4}
print(x < y, x == y, x > y)              # çıktı: False False False

frozenset: değiştirilemeyen küme

set değiştirilebilir olduğu için hash değeri yoktur; bu yüzden sözlük anahtarı ya da başka bir kümenin elemanı olamaz. frozenset ise oluşturulduktan sonra değiştirilemez ve hash edilebilirdir. Belgelere göre "kümelerin kümesini" temsil etmek istiyorsanız iç kümelerin frozenset olması gerekir. Değiştirmeyen tüm işlemler (birleşim, kesişim, alt küme sorguları, copy()) frozenset'te de vardır. set ile frozenset elemanlarına göre karşılaştırılır, yani aynı elemanlara sahiplerse eşittirler. İkisini karıştıran işlemlerde sonuç soldaki işlenenin tipinde olur.

python
izinler = {frozenset(['a', 'b']): 'temel', frozenset('abc'): 'tam'}
print(izinler[frozenset('ba')])    # çıktı: temel

kume_kumesi = {frozenset({1, 2}), frozenset({3})}
print(len(kume_kumesi))            # çıktı: 2

try:
    {{1, 2}}
except TypeError as hata:
    print(hata)   # çıktı: cannot use 'set' as a set element (unhashable type: 'set')

print(set('abc') == frozenset('abc'))             # çıktı: True
print(set('abc') in {frozenset('abc')})          # çıktı: True
print(type(frozenset('ab') | set('bc')).__name__) # çıktı: frozenset
print(type(set('bc') | frozenset('ab')).__name__) # çıktı: set

Belgelerdeki küçük ama kullanışlı bir ayrıntı: in, remove() ve discard() metotlarına argüman olarak bir set verdiğinizde Python, eşdeğer bir frozenset aramak için geçici bir tane oluşturur. Yukarıdaki set('abc') in {frozenset('abc')} satırının True vermesi bundandır. Kümeye eklemeye çalıştığınız {1, 2} ise hash edilemediği için hata verir; hata mesajının hangi tipin neden kullanılamadığını söyleyen bu biçimi Python 3.14 ile geldi.

Hash edilebilirlik: kümeye neler girebilir?

Küme elemanları, tıpkı sözlük anahtarları gibi hash edilebilir olmalıdır. Belgelerdeki Glossary tanımına göre bir nesne, ömrü boyunca değişmeyen bir hash değerine sahipse ve başka nesnelerle eşitlik karşılaştırması yapılabiliyorsa hash edilebilirdir; eşit olan nesnelerin hash değerleri de aynı olmak zorundadır. Python'un değiştirilemeyen yerleşik nesnelerinin çoğu hash edilebilir; liste ve sözlük gibi değiştirilebilir kaplar değildir; tuple ve frozenset gibi değiştirilemeyen kaplar ise yalnızca elemanları hash edilebiliyorsa hash edilebilir. Eşitlik kuralı yüzünden 1, 1.0 ve True kümede tek bir eleman sayılır.

python
print({1, 1.0, True})          # çıktı: {1}
print(len({(1, 2), (1, 2)}))   # çıktı: 1

try:
    {1}.add([2])
except TypeError as hata:
    print(hata)   # çıktı: cannot use 'list' as a set element (unhashable type: 'list')

Kendi sınıflarınızın nesneleri varsayılan olarak hash edilebilir: hepsi yalnızca kendisine eşittir ve hash değerleri kimliklerinden (id()) türetilir. Ancak veri modeli belgelerine göre bir sınıf __eq__() tanımlayıp __hash__() tanımlamazsa __hash__ otomatik olarak None yapılır ve nesneler hash edilemez hale gelir. Değere göre eşitlik tanımladıysanız aynı alanlardan bir hash de üretmeniz gerekir. Bunun ayrıntıları özel metotlar (dunder) yazısında, otomatik üreten bir araç olarak @dataclass(frozen=True) ise dataclasses yazısında.

python
class Nokta:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __eq__(self, diger):
        return (self.x, self.y) == (diger.x, diger.y)

try:
    {Nokta(1, 2)}
except TypeError as hata:
    print(hata)   # çıktı: cannot use 'Nokta' as a set element (unhashable type: 'Nokta')

class HashliNokta(Nokta):
    def __hash__(self):
        return hash((self.x, self.y))

print(len({HashliNokta(1, 2), HashliNokta(1, 2)}))   # çıktı: 1
print(len({object(), object()}))                     # çıktı: 2

Tekrarları temizlemek: set mi dict.fromkeys mi?

Python SSS, elemanların hepsi hash edilebiliyorsa bir listedeki tekrarları atmanın genellikle en hızlı yolunun list(set(liste)) olduğunu belirtir. Bu yöntemin bedeli sıradır: kümeler sırasız olduğu için sonuç orijinal sırayı korumaz. Sırayı korumak istiyorsanız sözlüklerin ekleme sırasını korumasından yararlanan list(dict.fromkeys(liste)) kalıbını kullanın. Sıralı ve tekrarsız bir sonuç için tutorial'daki deyimsel çözüm sorted(set(liste))'tir.

python
ziyaretler = ['ana', 'blog', 'ana', 'iletişim', 'blog', 'fiyat']
print(sorted(set(ziyaretler)))        # çıktı: ['ana', 'blog', 'fiyat', 'iletişim']
print(list(dict.fromkeys(ziyaretler)))  # çıktı: ['ana', 'blog', 'iletişim', 'fiyat']
print(len(set(ziyaretler)))           # çıktı: 4

Döngüde kümeyi değiştirmek

Bir küme üzerinde dönerken ona eleman ekler veya silerseniz Python RuntimeError fırlatır. Listelerdeki gibi çözüm, bir kopya üzerinde dönmek ya da yeni bir küme oluşturmaktır. Kümelerde genellikle en temiz yol, silinecekleri bir set comprehension ile seçip fark işlemiyle çıkarmaktır.

python
s = {1, 2, 3, 4}
try:
    for x in s:
        s.add(x * 10)
except RuntimeError as hata:
    print(hata)    # çıktı: Set changed size during iteration

s = {1, 2, 3, 4, 5, 6}
s -= {x for x in s if x % 2 == 0}
print(s)           # çıktı: {1, 3, 5}

Sözlük anahtarlarıyla küme işlemleri

dict.keys() görünümü elemanları benzersiz ve hash edilebilir olduğu için küme gibi davranır ve küme işleçlerini doğrudan destekler. İki sözlüğün ortak anahtarlarını bulmak için önce kümeye çevirmeniz gerekmez. Ayrıntılar sözlük metotları yazısında.

python
eski = {'ad': 'Ece', 'yas': 30, 'sehir': 'İzmir'}
yeni = {'ad': 'Ece', 'yas': 31, 'meslek': 'mühendis'}
print(sorted(eski.keys() & yeni.keys()))   # çıktı: ['ad', 'yas']
print(sorted(yeni.keys() - eski.keys()))   # çıktı: ['meslek']
print(sorted(eski.keys() ^ yeni.keys()))   # çıktı: ['meslek', 'sehir']

Uygulamalı mini örnek: iki günün ziyaretçileri

Bir sitenin iki gündeki ziyaretçi kullanıcı adlarını karşılaştıralım: her iki gün gelenler, yalnızca bugün gelen yeni kullanıcılar, dün gelip bugün gelmeyenler ve iki günde toplam kaç farklı kullanıcı olduğu. Her soru tek bir küme işlemiyle cevaplanıyor.

python
dun = {'ali', 'banu', 'cem', 'deniz'}
bugun = {'banu', 'deniz', 'ece', 'fırat'}

print('Her iki gün:', sorted(dun & bugun))   # çıktı: Her iki gün: ['banu', 'deniz']
print('Yeni gelen:', sorted(bugun - dun))    # çıktı: Yeni gelen: ['ece', 'fırat']
print('Gelmeyen:', sorted(dun - bugun))      # çıktı: Gelmeyen: ['ali', 'cem']
print('Tek gün:', sorted(dun ^ bugun))       # çıktı: Tek gün: ['ali', 'cem', 'ece', 'fırat']
print('Toplam farklı:', len(dun | bugun))    # çıktı: Toplam farklı: 6

Python 3.15 ve kümeler

Python 3.15'te PEP 798 ile comprehension'larda açma (unpacking) desteklendi. Bu sayede bir küme listesini tek bir kümede birleştirmek için iç içe döngü yazmak yerine {*s for s in kumeler} yazabilirsiniz; bu ifade {x for s in kumeler for x in s} ile eşdeğerdir. Söz diziminin tamamı list comprehension yazısında. Yine 3.15 ile collections.Counter nesnelerine de simetrik fark işleci ^ eklendi; collections modülü yazısında görebilirsiniz.

python
kumeler = [{1, 2}, {2, 3}, {3, 4}]
print({*s for s in kumeler})                   # çıktı: {1, 2, 3, 4}
print({*s for s in kumeler} == set().union(*kumeler))   # çıktı: True

Hata mesajları tarafında da 3.15 kümeleri ilgilendiren bir kolaylık getirdi: bir listeye kümelere özgü add() metodunu çağırdığınızda Python, aradığınız tipin muhtemelen küme olduğunu söylüyor. Aşağıdaki çıktı Python 3.15.2'den alınmıştır, dosya yolu ornek.py olarak kısaltıldı. Diğer örnekler için sık görülen hata mesajları yazısına bakabilirsiniz.

python
goruldu = []
goruldu.add('ana sayfa')
# Traceback (most recent call last):
#   File "ornek.py", line 2, in <module>
#     goruldu.add('ana sayfa')
#     ^^^^^^^^^^^
# AttributeError: 'list' object has no attribute 'add'. Did you mean to use a 'set' object?

Sık yapılan hatalar

Sık yapılan hatalar: Boş küme için {} yazmak, Kümede indeksle eleman almaya çalışmak, Küme yazdırma sırasının sabit olduğunu varsaymak, Liste veya sözlük gibi hash edilemeyen bir...
  • Boş küme için {} yazmak: bu bir sözlüktür, set() kullanın.
  • Kümede indeksle eleman almaya çalışmak: kümeler sırasızdır, gerekiyorsa sorted() ile listeye çevirin.
  • Küme yazdırma sırasının sabit olduğunu varsaymak: string içeren kümelerin sırası çalıştırmadan çalıştırmaya değişebilir.
  • Liste veya sözlük gibi hash edilemeyen bir değeri kümeye eklemek: tuple veya frozenset'e çevirin.
  • remove() ile discard()'ı karıştırmak: olmayan eleman için ilki hata verir.
  • set('abc') & 'cbs' gibi işleç biçimine küme olmayan bir değer vermek: metot biçimini kullanın.
  • set('kelime') yazıp kelimenin kendisinin eleman olacağını sanmak: string harflerine ayrılır, {'kelime'} yazın.

Sık Sorulan Sorular

Kümede arama listeden neden hızlı?

Python belgelerindeki Glossary sayfasına göre kümeler de sözlükler gibi elemanların hash değerini içeride kullanır. Python SSS, hash tablosunda bir kaydın konumunun hash değerinden hesaplandığını, bu yüzden aramanın tabloyu baştan sona taramayı gerektirmediğini anlatır; listede ise in elemanları sırayla karşılaştırır. Belgeler kümelerin temel kullanım alanlarından birini tam olarak bu, yani hızlı üyelik testi olarak tanımlar. Çok sayıda in sorgusu yapacağınız büyük bir koleksiyonu bir kez kümeye çevirmek bu yüzden mantıklıdır.

Kümenin ilk elemanını nasıl alırım?

Kümelerde "ilk" eleman kavramı yoktur. Herhangi bir elemanı almak için next(iter(s)) kullanabilirsiniz; elemanı aynı zamanda çıkarmak istiyorsanız pop() rastgele bir tanesini döndürür. Belirli bir sıraya ihtiyacınız varsa min(s), max(s) ya da sorted(s)[0] gibi açık bir ölçüt kullanın.

set ile frozenset arasında performans farkı var mı?

Belgeler iki tip arasında bir hız farkı tanımlamaz; seçim değiştirilebilirlik ihtiyacına göre yapılır. İçeriği sonradan değişecekse set, sözlük anahtarı, küme elemanı ya da sabit bir değer olarak kullanılacaksa frozenset seçin.

Kümelerin elemanlarını nasıl sayarım?

Küme her elemanı yalnızca bir kez tuttuğu için sayım bilgisini kaybeder. Tekrar sayılarına ihtiyacınız varsa çoklu küme (multiset) gibi çalışan collections.Counter kullanın; o da birleşim ve kesişim işleçlerini destekler.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Python öğrenme rehberi · Sözlük metotları · Liste metotları · Tuple ve hash · Set comprehension · Counter