Python 3.10 ile gelen match deyimi, bir değeri sırayla yazılmış case desenleriyle karşılaştırır ve yalnızca ilk eşleşen bloğu çalıştırır. İlk bakışta C veya JavaScript'teki switch'e benzer, ama resmî Tutorial'ın ifadesiyle Rust veya Haskell'deki desen eşleştirmeye (pattern matching) daha yakındır: Desenler yalnızca sabit değerleri karşılaştırmaz, listelerin elemanlarını, sözlüklerin anahtarlarını ve nesnelerin özelliklerini değişkenlere ayırarak yakalayabilir. case _: her şeyle eşleşen joker (wildcard) desendir; hiçbir desen eşleşmezse hiçbir blok çalışmaz ve hata da oluşmaz.

Bu yazıda desen türlerini (literal, yakalama, dizi, sözlük, sınıf, OR ve AS desenleri), koruma koşullarını (guard) ve en sık yapılan hataları Language Reference'ın match bölümüne ve Tutorial'a dayanarak anlatıyoruz. Bütün örnekler Python 3.15.2 ile çalıştırıldı. Basit koşullar için if/elif/else yazısı daha uygun bir başlangıçtır.

En basit kullanım: literal desenler

En basit kullanım: literal desenler

En basit biçimde match'in yanındaki değer (konu, subject) her case'teki sabitle karşılaştırılır. | işaretiyle birden fazla sabiti tek desende birleştirebilirsiniz; buna OR deseni denir. case _: en sona konur ve geri kalan her durumu yakalar.

python
def http_durum(kod):
    match kod:
        case 200:
            return 'Tamam'
        case 400:
            return 'Hatalı istek'
        case 401 | 403:
            return 'Yetki yok'
        case 404:
            return 'Bulunamadı'
        case _:
            return 'Bilinmeyen durum'

for k in (200, 403, 404, 500):
    print(k, http_durum(k))
# çıktı: 200 Tamam
# çıktı: 403 Yetki yok
# çıktı: 404 Bulunamadı
# çıktı: 500 Bilinmeyen durum

C'deki switch'ten farklı olarak break yazmanız gerekmez ve bir bloktan diğerine düşme (fall-through) olmaz: İlk eşleşen blok çalışır, gerisi denenmez. Hiçbir desen eşleşmezse match sessizce biter:

python
def kontrol(x):
    match x:
        case 1:
            print('bir')
    print('match bitti')

kontrol(5)    # çıktı: match bitti

match ve case yumuşak anahtar kelimedir

Language Reference'a göre match, case ve _ yumuşak anahtar kelimelerdir (soft keyword): Yalnızca bu deyimin içinde özel anlam taşırlar. Bu sayede 3.10'dan önce yazılmış ve match adında değişken veya fonksiyon kullanan kodlar bozulmadı. Örneğin re modülündeki re.match() fonksiyonu hâlâ bu adı taşır (Python 3.15 yeni kod için re.prefixmatch() adını öneriyor, ama belgelere göre eski ad kaldırılmayacak).

python
match = 'değişken adı olarak da kullanılabilir'
print(match)     # çıktı: değişken adı olarak da kullanılabilir

Literal desenler == ile, None/True/False is ile karşılaştırılır

Belgelere göre literal desenlerin çoğu eşitlikle (==) karşılaştırılır; None, True ve False tekil nesneleri ise kimlikle (is). Bunun az bilinen bir sonucu vardır: Python'da True == 1 doğru olduğu için case 1: deseni True değeriyle de eşleşir, ama case True: deseni 1 ile eşleşmez. Desenlerde metin (normal, ham ve üç tırnaklı) ve bayt literalleri kullanılabilir, f-string ve t-string kullanılamaz. Python 3.15 ile literal desenlerde, zaten desteklenen eksi işaretine ek olarak tekli artı işareti de (case +1:) kabul edilmeye başladı.

python
match True:
    case 1:
        print('case 1, True ile eşleşti')
match 1:
    case True:
        print('eşleşti')
    case _:
        print('case True, 1 ile eşleşmedi')
match 1:
    case +1:                     # Python 3.15 ve sonrası
        print('artı işaretli literal')
# çıktı: case 1, True ile eşleşti
# çıktı: case True, 1 ile eşleşmedi
# çıktı: artı işaretli literal

Yakalama desenleri ve dizi desenleri

Desende tek başına yazılan bir ad (örneğin y) bir yakalama desenidir (capture pattern): Her zaman eşleşir ve karşılık gelen değeri o ada atar. Tutorial, desenleri bir atamanın sol tarafının genişletilmiş hâli gibi okumayı önerir: case (x, y): deseni, (x, y) = nokta açma atamasına kavramsal olarak benzer. Literal ve yakalama desenleri bir arada kullanılabilir:

python
def nokta(p):
    match p:
        case (0, 0):
            return 'Başlangıç noktası'
        case (0, y):
            return f'Y ekseni üzerinde, y={y}'
        case (x, 0):
            return f'X ekseni üzerinde, x={x}'
        case (x, y):
            return f'x={x}, y={y}'
        case _:
            return 'Nokta değil'

for p in [(0, 0), (0, 5), (3, 0), [2, 7], (1, 2, 3), 'ab']:
    print(nokta(p))
# çıktı: Başlangıç noktası
# çıktı: Y ekseni üzerinde, y=5
# çıktı: X ekseni üzerinde, x=3
# çıktı: x=2, y=7
# çıktı: Nokta değil
# çıktı: Nokta değil

Çıktıdaki üç ayrıntı belgelerdeki kuralları gösterir. Birincisi, demet ve liste desenleri aynı anlama gelir ve herhangi bir diziyle eşleşir; bu yüzden [2, 7] listesi (x, y) deseniyle eşleşti. İkincisi, yıldızsız bir dizi deseni sabit uzunlukludur; üç elemanlı demet iki elemanlı desene uymadı. Üçüncüsü, dizi desenleri str, bytes ve bytearray ile hiç eşleşmez; 'ab' iki karakterli olsa da (x, y) desenine girmedi. Yineleyiciler de dizi sayılmaz.

Yakalanan adlar nereye atanır? Language Reference'a göre yakalama desenleri, atama ifadesi (:=) ile aynı kapsam kurallarını izler: Ad, en yakın fonksiyon kapsamında yerel değişken olur; o fonksiyonda global veya nonlocal bildirimi varsa ona uyulur. Yani case bloğu ayrı bir kapsam oluşturmaz; kapsam kuralları kapsam yazısında anlatılıyor.

Yıldızlı desenler ve komut ayrıştırma

Dizi desenleri, açma atamalarındaki gibi yıldızlı bir alt desen alabilir: [ilk, *kalan] en az bir elemanlı her diziyle eşleşir ve kalan elemanları liste olarak yakalar. Belgelere göre bir dizi deseninde en fazla bir yıldızlı alt desen olabilir ve bu desen herhangi bir konumda durabilir. *_ kalan elemanları bağlamadan yok sayar. Bu özellik, kullanıcının yazdığı bir komutu kelimelerine ayırıp işlemek için çok uygundur:

python
def komut_isle(satir):
    match satir.split():
        case []:
            return 'Boş komut'
        case ['çık' | 'q']:
            return 'Çıkılıyor'
        case ['git', yon] if yon in ('kuzey', 'güney', 'doğu', 'batı'):
            return f'{yon} yönüne gidiliyor'
        case ['git', yon]:
            return f'Bilinmeyen yön: {yon}'
        case ['al', *esyalar] if esyalar:
            return 'Alındı: ' + ', '.join(esyalar)
        case ['al']:
            return 'Ne alınacak?'
        case [komut, *_]:
            return f'Bilinmeyen komut: {komut}'

for s in ['', 'q', 'git kuzey', 'git yukarı', 'al anahtar fener', 'al', 'zıpla yüksek']:
    print(komut_isle(s))
# çıktı: Boş komut
# çıktı: Çıkılıyor
# çıktı: kuzey yönüne gidiliyor
# çıktı: Bilinmeyen yön: yukarı
# çıktı: Alındı: anahtar, fener
# çıktı: Ne alınacak?
# çıktı: Bilinmeyen komut: zıpla

['çık' | 'q'] deseni, tek elemanlı ve o elemanı 'çık' ya da 'q' olan listelerle eşleşir; OR deseni bir alt desen olarak iç içe kullanılabilir. Belgelerin bir notu: Köşeli parantez içindeki tek desen ([3 | 4]) hâlâ bir dizi desenidir, ama sondaki virgül olmadan normal parantez içindeki tek desen ((3 | 4)) yalnızca bir gruplama desenidir.

Koruma koşulu (guard)

Koruma koşulu (guard)

Bir desenden sonra if koşul yazarak ek bir şart ekleyebilirsiniz; buna koruma koşulu (guard) denir. Language Reference akışı şöyle tanımlar: Önce desen denenir; desen başarısızsa koşul hiç değerlendirilmez. Desen başarılıysa koşul değerlendirilir; doğruysa blok seçilir, yanlışsa bir sonraki case denenir. Koşul değerlendirilirken desenin yaptığı bütün bağlamalar yapılmış olur, bu yüzden koşul yakalanan değişkenleri kullanabilir. Koşul bir istisna yükseltirse istisna dışarı çıkar. Yukarıdaki ['git', yon] if yon in (...) satırı tam olarak bunu yapıyor: Yön geçersizse koşul yanlış olur ve sıradaki ['git', yon] deseni devreye girer.

Konuyu demet olarak vermek

match'in yanındaki ifade virgül içeriyorsa Python normal kurallarla bir demet oluşturur. Bu, iki değeri birlikte değerlendirmek için kısa bir yoldur. Aşağıdaki FizzBuzz uyarlaması bölümden kalanları demet olarak eşleştirir:

python
def fizzbuzz(n):
    match n % 3, n % 5:
        case 0, 0:
            return 'FizzBuzz'
        case 0, _:
            return 'Fizz'
        case _, 0:
            return 'Buzz'
        case _:
            return str(n)

print(' '.join(fizzbuzz(i) for i in range(1, 16)))
# çıktı: 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz

Sözlük (mapping) desenleri

{'anahtar': desen} biçimindeki desenler sözlük gibi eşleme (mapping) nesneleriyle eşleşir. Dizi desenlerinden farklı olarak fazla anahtarlar yok sayılır: Desende geçen anahtarların sözlükte bulunması ve değerlerinin alt desenlere uyması yeterlidir. Kalan anahtarları toplamak için en sona **ad yazılabilir; **_ ise gereksiz olduğu için izin verilmez. Bu desenler JSON'dan gelen olay veya mesaj verilerini işlemek için çok kullanışlıdır:

python
def olay_isle(olay):
    match olay:
        case {'tur': 'giris', 'kullanici': ad}:
            return f'{ad} giriş yaptı'
        case {'tur': 'satis', 'tutar': int(tutar) | float(tutar)} if tutar > 0:
            return f'Satış: {tutar} TL'
        case {'tur': 'hata', **ayrinti}:
            return f'Hata ayrıntısı: {ayrinti}'
        case {'tur': tur}:
            return f'İşlenmeyen olay türü: {tur}'
        case _:
            return 'Geçersiz olay'

print(olay_isle({'tur': 'giris', 'kullanici': 'ece', 'zaman': '10:00'}))
print(olay_isle({'tur': 'satis', 'tutar': 250}))
print(olay_isle({'tur': 'satis', 'tutar': '250'}))
print(olay_isle({'tur': 'hata', 'kod': 500, 'mesaj': 'zaman aşımı'}))
print(olay_isle(['tur', 'giris']))
# çıktı: ece giriş yaptı
# çıktı: Satış: 250 TL
# çıktı: İşlenmeyen olay türü: satis
# çıktı: Hata ayrıntısı: {'kod': 500, 'mesaj': 'zaman aşımı'}
# çıktı: Geçersiz olay

Metin olarak gelen '250' tutarı int(tutar) | float(tutar) alt desenine uymadığı için bir sonraki genel desene düştü; desenler böylece tip doğrulamasını da üstlenebilir. Belgelere göre eşleştirme, sözlüğün iki argümanlı get() metoduyla yapılır; yani anahtarın gerçekten sözlükte bulunması gerekir, defaultdict gibi yapılarda __missing__() ile anında oluşturulan değerler sayılmaz. Aynı sabit anahtarı bir desende iki kez yazmak SyntaxError verir. JSON verisini Python'a almak için json modülü yazısına bakın.

Sınıf desenleri

Verilerinizi sınıflarla tutuyorsanız, sınıf adından sonra kurucu çağrısına benzeyen bir argüman listesi yazarak hem tipi kontrol edebilir hem de özellikleri yakalayabilirsiniz. Language Reference'a göre Sinif(...) deseni önce isinstance() testi yapar, sonra ad=desen biçimindeki her anahtar kelimeyi nesnenin özelliği olarak arar. Konumsal alt desenler ise sınıfın __match_args__ özelliğindeki sıraya göre özellik adlarına çevrilir. Sinif() gibi argümansız bir desen yalnızca tipi kontrol eder.

python
class Nokta:
    __match_args__ = ('x', 'y')
    def __init__(self, x, y):
        self.x = x
        self.y = y

def nerede(n):
    match n:
        case Nokta(x=0, y=0):
            return 'Orijin'
        case Nokta(0, y):
            return f'Y ekseni, y={y}'
        case Nokta(x, y) if x == y:
            return f'Köşegen üzerinde, {x}'
        case Nokta():
            return 'Başka bir yerde'
        case _:
            return 'Nokta değil'

for n in (Nokta(0, 0), Nokta(0, 4), Nokta(2, 2), Nokta(1, 5), (1, 5)):
    print(nerede(n))
# çıktı: Orijin
# çıktı: Y ekseni, y=4
# çıktı: Köşegen üzerinde, 2
# çıktı: Başka bir yerde
# çıktı: Nokta değil

Tutorial'ın önemli bir uyarısı: Desende yalnızca tek başına duran adlar (yukarıdaki y, x) atanır. Noktalı adlar (Renk.KIRMIZI), özellik adları (x= kısmı) ve sınıf adları (Nokta) asla atanmaz. dataclass ile oluşturulan sınıflar __match_args__'ı otomatik tanımlar, bu yüzden konumsal desenleri ek bir ayar yapmadan kullanabilirsiniz:

python
from dataclasses import dataclass

@dataclass
class Daire:
    yaricap: float

@dataclass
class Dikdortgen:
    en: float
    boy: float

def tarif(sekil):
    match sekil:
        case Daire(r):
            return f'Daire, yarıçap {r}'
        case Dikdortgen(en, boy) if en == boy:
            return f'Kare, kenar {en}'
        case Dikdortgen(en, boy):
            return f'Dikdörtgen, {en} x {boy}'

print(Dikdortgen.__match_args__)     # çıktı: ('en', 'boy')
print(tarif(Daire(3)))               # çıktı: Daire, yarıçap 3
print(tarif(Dikdortgen(4, 4)))       # çıktı: Kare, kenar 4
print(tarif(Dikdortgen(2, 5)))       # çıktı: Dikdörtgen, 2 x 5

Yerleşik tiplerle tip kontrolü

bool, int, float, str, bytes, bytearray, list, tuple, dict, set, frozenset ve Python 3.15'te eklenen frozendict için konumsal alt desen özel çalışır: Tek bir alt desen, özelliğe değil nesnenin tamamına uygulanır. Belgelerdeki örneğe göre int(0|1) deseni 0 değeriyle eşleşir ama 0.0 ile eşleşmez. Burada dikkat edilmesi gereken nokta bool'un int'in alt sınıfı olmasıdır: int() deseni True ile de eşleşir, bu yüzden bool() desenini önce yazın.

python
def tip_adi(deger):
    match deger:
        case bool():
            return 'mantıksal'
        case int() | float():
            return 'sayı'
        case str() as metin if not metin:
            return 'boş metin'
        case str():
            return 'metin'
        case [*_]:
            return 'dizi'
        case {}:
            return 'eşleme'
        case None:
            return 'None'
        case _:
            return 'diğer'

for d in (True, 3, 2.5, '', 'abc', [1], (1, 2), {'a': 1}, None, {1, 2}):
    print(repr(d), tip_adi(d))
# çıktı: True mantıksal
# çıktı: 3 sayı
# çıktı: 2.5 sayı
# çıktı: '' boş metin
# çıktı: 'abc' metin
# çıktı: [1] dizi
# çıktı: (1, 2) dizi
# çıktı: {'a': 1} eşleme
# çıktı: None None
# çıktı: {1, 2} diğer

case {}: deseni boş sözlük demek değildir; fazla anahtarlar yok sayıldığı için her eşleme nesnesiyle eşleşir. Küme (set) ise ne dizi ne eşleme olduğu için joker desene düştü. str() as metin kısmı bir AS desenidir: Sol taraftaki desen eşleşirse konunun tamamı sağdaki ada atanır.

AS deseni iç içe yapılarda özellikle işe yarar: Bir alt yapıyı hem parçalarına ayırıp hem de bütün olarak saklamanızı sağlar. Belgelere göre as'in sağındaki ad _ olamaz.

python
match [3, (1, 2)]:
    case [int() as adet, (x, y) as cift]:
        print(adet, x, y, cift)      # çıktı: 3 1 2 (1, 2)

En sık hata: sabit sanılan yakalama deseni

case'te tek başına yazılan ad bir sabit değil, yakalama desenidir. Bir sabitle karşılaştırmak isteyip case KIRMIZI: yazarsanız bu desen her değerle eşleşir ve KIRMIZI değişkeninin üzerine yazar. Python bu hatanın bir kısmını derleme sırasında yakalar: Böyle bir desenin altına başka case yazarsanız SyntaxError: name capture 'KIRMIZI' makes remaining patterns unreachable alırsınız. Tek case varsa hata olmaz, ama sonuç yanlıştır:

python
KIRMIZI = 'kırmızı'
renk = 'mavi'
match renk:
    case KIRMIZI:                  # sabit değil, yakalama deseni!
        print('Eşleşti:', KIRMIZI)
print(KIRMIZI)
# çıktı: Eşleşti: mavi
# çıktı: mavi

Tutorial'ın çözümü, sabitleri noktalı adlarla kullanmaktır; noktalı adlar yakalama değil değer deseni sayılır ve == ile karşılaştırılır. En temiz yol bir Enum tanımlamaktır:

python
from enum import Enum

class Renk(Enum):
    KIRMIZI = 'kırmızı'
    MAVI = 'mavi'

match Renk('mavi'):
    case Renk.KIRMIZI:
        print('Kırmızı seçildi')
    case Renk.MAVI:
        print('Mavi seçildi')
# çıktı: Mavi seçildi

Enum kullanımının ayrıntıları enum yazısında anlatılıyor.

Diğer kurallar

Diğer kurallar: Her şeyi yakalayan blok en sonda olmalı, Bağlamalar match'ten sonra da yaşar, OR desenindeki her seçenek aynı adları bağlamalı, Bir desende aynı ad iki kez...
  • Her şeyi yakalayan blok en sonda olmalı: Belgelere göre bir match'te koşulsuz ve her zaman eşleşen en fazla bir blok olabilir ve son sırada durmalıdır. case _: altına başka blok yazmak SyntaxError: wildcard makes remaining patterns unreachable verir.
  • Bağlamalar match'ten sonra da yaşar: Başarılı bir eşleşmede atanan adlar blok bittikten sonra da kullanılabilir. Başarısız denemelerde ise bazı alt desenler bağlama yapmış olabilir; belgeler bunlara güvenmemenizi açıkça söyler.
  • OR desenindeki her seçenek aynı adları bağlamalı: case (x, 0) | (0, x): geçerlidir; seçeneklerden biri y bağlıyorsa diğeri de bağlamalıdır.
  • Bir desende aynı ad iki kez bağlanamaz: case x, x: geçersizdir.
  • Desenler değerlendirilme sayısına güvenmeyin: Belgelere göre yorumlayıcı değerleri önbelleğe alabilir veya tekrarlanan değerlendirmeleri atlayabilir.

match mi, if/elif mi?

DurumUygun yapı
Tek bir değeri birkaç sabitle karşılaştırmakİkisi de olur; match daha düzenli görünür
Aralık kontrolü (x > 10 gibi)if/elif (match içinde guard gerekir)
Listenin uzunluğuna ve elemanlarına göre dallanmakmatch (dizi desenleri)
JSON benzeri sözlüklerin yapısına göre dallanmakmatch (sözlük desenleri)
Nesnenin tipine ve özelliklerine göre dallanmakmatch (sınıf desenleri)
Python 3.9 veya daha eski sürümü desteklemekif/elif (match 3.10 ile geldi)

Sık Sorulan Sorular

Python'da switch case var mı?

Ayrı bir switch deyimi yoktur. Python 3.10'dan itibaren match/case bu işi görür; daha eski sürümlerde ve basit durumlarda if/elif/else zinciri kullanılır.

case içinde break yazmam gerekir mi?

Hayır. Yalnızca ilk eşleşen blok çalışır ve sonraki bloklara geçiş olmaz.

Hiçbir case eşleşmezse ne olur?

Hiçbir blok çalışmaz ve program match'ten sonraki satırdan devam eder; hata oluşmaz. Varsayılan davranış için en sona case _: ekleyin.

Bir değişkenin değeriyle nasıl karşılaştırırım?

Çıplak ad yakalama deseni olduğu için ya noktalı bir ad kullanın (Renk.KIRMIZI, ayar.SINIR) ya da case x if x == hedef: gibi bir koruma koşulu yazın.

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 · if/elif/else koşulları · Enum kullanımı · dataclasses · Tuple ve unpacking · Sözlük metotları · class ve nesne