Berk Akademi
Ana Sayfa
ÖZEL KODLAMA DERSLERİ
Yazılım Özel Ders
Tüm birebir programlara genel bakış
Python Yazılım Kursu
Sıfırdan ileri seviyeye birebir Python
Java Yazılım Kursu
OOP odaklı birebir Java eğitimi
AP Computer Science Principles
AP CSP sınav hazırlığı
GRUP DERSLERİ
Python & Django Masterclass
SINIRLI KONTENJAN
Java & Spring Boot Masterclass
SINIRLI KONTENJAN
C# .NET Masterclass
SINIRLI KONTENJAN
VİDEO DERSLER
Sıfırdan Temel Python Kursu
Kendi Hızında Öğren
ÜCRETSİZ
Seviye Testi
Ücretsiz — Python, Java, algoritma seviye testleri
Kariyerini Keşfet
Sertifika Doğrula
Belge numarası ve soyad ile doğrulama

Python KeyError Hatasını Sistematik Olarak Teşhis Etme

python-keyerror-hatasi-teshis-ve-cozum
Bu yazıda neler var?
  1. KeyError nedir ve hata mesajındaki eksik anahtar nasıl okunur?
  2. KeyError kaynağı üç aşamada nasıl teşhis edilir?
  3. Eksik anahtar hangi aşamada ortaya çıkar?
  4. Köşeli parantez, get(), doğrulama ve try-except nasıl karşılaştırılır?
  5. price ve unit_price uyuşmazlığı kod üzerinde nasıl düzeltilir?
  6. Doğru çözüm yaklaşımı hangi durumda seçilmelidir?
  7. Sık Sorulan Sorular

Python KeyError, bir sözlükte istenen anahtar bulunmadığında oluşur. Örneğin kod `product["price"]` ifadesiyle `price` anahtarını arıyor, ancak sözlükte bu anahtar bulunmuyorsa Python `KeyError: 'price'` mesajını üretir. Hatanın doğru çözümü, eksik anahtarın neden bulunmadığını teşhis ettikten sonra alanın zorunlu veya isteğe bağlı olmasına göre belirlenir.

Bu nedenle yalnızca hatayı susturmak yerine traceback bilgisini, gerçek sözlük anahtarlarını ve verinin üretildiği noktayı birlikte incelemek gerekir. Aşağıdaki yaklaşım, hatanın yazım yanılgısından mı, eksik veriden mi yoksa beklenmeyen veri yapısından mı kaynaklandığını ayırt etmeye yardımcı olur.

KeyError nedir ve hata mesajındaki eksik anahtar nasıl okunur?

Python’da sözlükler, anahtar-değer ilişkisiyle çalışan veri yapılarıdır. Bir anahtarın karşılığındaki değere ulaşmak için çoğunlukla köşeli parantez kullanılır:

product = {
    "name": "Klavye",
    "unit_price": 750
}

print(product["price"])

Bu örnekte sözlükte name ve unit_price anahtarları vardır. Kod ise price anahtarını arar. Aranan anahtar bulunamadığı için program çalışmayı durdurur ve benzer bir hata verir:

Traceback (most recent call last):
  File "urun.py", line 6, in <module>
    print(product["price"])
          ~~~~~~~^^^^^^^^^
KeyError: 'price'

Buradaki KeyError: 'price' ifadesinde price, değeri eksik olan alanı değil, sözlük içinde aranan fakat bulunamayan anahtarı gösterir. Bu ayrım önemlidir. Python henüz price anahtarının değerinin boş, sıfır veya geçersiz olduğunu söylemiyor; önce böyle bir anahtarın sözlükte bulunup bulunmadığını kontrol ediyor.

Traceback bilgisi nasıl okunur?

Traceback, hatanın meydana geldiği noktaya ulaşmak için izlenebilecek teknik kaydı gösterir. Başlangıç ve orta seviyede şu üç bölümü ayırmak yeterlidir:

  • Dosya adı: Hatanın hangi Python dosyasında oluştuğunu gösterir.
  • Satır numarası: İncelenmesi gereken kod satırını belirtir.
  • Erişim ifadesi: Sözlükte hangi anahtara ulaşılmaya çalışıldığını gösterir.

Örnekte urun.py dosyasının 6. satırında product["price"] ifadesi çalıştırılmıştır. Dolayısıyla ilk soru “Python neden fiyat değerini bulamadı?” değil, “product sözlüğünde gerçekten price adlı bir anahtar var mı?” olmalıdır.

Bir anahtarın bulunamaması farklı nedenlerden kaynaklanabilir. En sık karşılaşılan durumlar şunlardır:

  • Anahtar adı farklı yazılmış olabilir: Kodda price, veride unit_price bulunabilir.
  • Büyük-küçük harf farkı olabilir: Price, price ile aynı anahtar değildir.
  • Veri gerçekten eksik olabilir: Bazı ürün kayıtlarında fiyat alanı hiç gönderilmemiş olabilir.
  • Veri yapısı değişmiş olabilir: Kod bir sözlük beklerken dışarıdan iç içe sözlükler, liste veya farklı alan adları gelmiş olabilir.
  • Dönüştürme sırasında alan yeniden adlandırılmış olabilir: JSON, form verisi veya başka bir fonksiyon üzerinden geçen kayıt farklı bir anahtar adıyla oluşturulmuş olabilir.

Örneğin şu iki sözlük görünüşte benzer bir bilgiyi taşır, ancak Python açısından aynı değildir:

first_product = {"price": 750}
second_product = {"unit_price": 750}

print(first_product["price"])       # 750
print(second_product["price"])      # KeyError

İkinci sözlükte fiyat bilgisi vardır; fakat bu bilgi price anahtarında değil, unit_price anahtarındadır. Bu nedenle “değer var mı?” sorusundan önce “değer hangi anahtar altında tutuluyor?” sorusu sorulmalıdır.

KeyError ile değer hatasını birbirinden ayırmak

KeyError, anahtar arama aşamasında oluşur. Sözlükte anahtar bulunur ancak karşılığındaki değer beklenen türde değilse farklı bir hata ortaya çıkabilir:

product = {
    "price": None
}

print(product["price"])       # Anahtar var, sonuç None
print(product["price"] + 10)  # TypeError

Bu örnekte price anahtarı mevcuttur. Sorun, anahtarın bulunmaması değil, değerinin sayısal bir değer olmamasıdır. Dolayısıyla hata mesajını dikkatli okumak, çözüm yolunu doğrudan etkiler:

  • KeyError: İstenen sözlük anahtarı bulunamadı.
  • TypeError: Değer üzerinde yapılan işlem, değer türüyle uyumlu değil.
  • ValueError: Değerin türü uygun olsa da içeriği beklenen biçimde değil.

KeyError kaynağı üç aşamada nasıl teşhis edilir?

KeyError kaynağı üç aşamada nasıl teşhis edilir?

KeyError gördüğünüzde hemen get() kullanmak veya try-except ile hatayı yakalamak her zaman doğru ilk adım değildir. Bu yöntemler bazı durumlarda yararlı olsa da, teşhis tamamlanmadan eklenirse sözlüğün neden beklenenden farklı geldiğini gizleyebilir. Daha güvenli yaklaşım, hatayı üç aşamada incelemektir.

  1. Hata mesajındaki eksik anahtarı belirleyin. KeyError: 'price' mesajındaki price ifadesini aynen not edin. Kodun hangi satırında bu anahtarın istendiğini bulun ve yazımın doğru olup olmadığını kontrol edin.

    Bu aşamada sorulacak temel soru şudur: “Kod gerçekten hangi anahtarı arıyor?” price ile unit_price, product_price veya Price aynı kabul edilmez.

  2. Sözlüğün gerçek anahtarlarını kontrollü biçimde inceleyin. Varsayım yapmak yerine keys(), repr() veya seçilmiş bir çıktı kullanın:

    product = {"name": "Klavye", "unit_price": 750}
    
    print("Anahtarlar:", list(product.keys()))
    print("Sözlüğün gerçek hâli:", repr(product))
    
    if "price" not in product:
        print("'price' anahtarı bulunamadı")

    Beklenen çıktı, sözlükte gerçekten hangi alanların bulunduğunu gösterir:

    Anahtarlar: ['name', 'unit_price']
    Sözlüğün gerçek hâli: {'name': 'Klavye', 'unit_price': 750}
    'price' anahtarı bulunamadı

    repr() özellikle görünmeyen boşlukları, beklenmedik karakterleri veya değerin gerçek türünü fark etmek için yararlıdır. Örneğin "price " ile "price" farklı anahtarlardır.

  3. Verinin üretildiği veya dönüştürüldüğü noktayı kontrol edin. Sözlük doğrudan kod içinde mi oluşturuldu, bir fonksiyondan mı döndü, JSON verisinden mi geldi, yoksa başka bir dönüşümden mi geçti? price beklenirken unit_price bulunuyorsa, yalnızca erişim satırına bakmak yeterli değildir.

    Bu aşamada şu sorular sorulmalıdır:

    • Sözlüğü oluşturan fonksiyon hangi anahtar adını kullanıyor?
    • Veri dış kaynaktan gelirken alan adı değişiyor mu?
    • JSON içindeki alan, Python sözlüğüne dönüştürüldüğünde beklenen yerde mi?
    • Bütün kayıtlarda aynı anahtar yapısı var mı, yoksa yalnızca bazı kayıtlarda mı eksik?
    • Bir dönüştürme fonksiyonu price alanını unit_price olarak yeniden adlandırmış olabilir mi?

Örneğin şu akışta hata, erişim satırında görünse de kök neden veriyi oluşturan fonksiyondadır:

def create_product():
    return {
        "name": "Klavye",
        "unit_price": 750
    }

product = create_product()
print(product["price"])

Burada print() satırı hatayı görünür hâle getirir; ancak veri yapısındaki uyuşmazlık create_product() fonksiyonunda ortaya çıkmıştır. Kodun bir bölümünde unit_price üretilirken başka bir bölümünde price beklenmektedir. Çözüm, eksik anahtara rastgele varsayılan değer eklemekten önce bu iki kararın aynı adlandırma kuralına getirilmesidir.

Bu tür hataları daha hızlı okuyabilmek için Python bilgi testi gibi kısa alıştırmalarla sözlükler, koşullar ve hata mesajları üzerine temel pratiği pekiştirmek yararlı olabilir. Amaç yalnızca hatayı ezberlenmiş bir yöntemle kapatmak değil, kodun hangi veriyi beklediğini ve gerçekte hangi veriyi aldığını karşılaştırabilmektir.

Teşhis sırasında doğrudan product.get("price", 0) yazmak bazen hatayı ortadan kaldırır; fakat bu tercih kök nedeni saklayabilir. Eğer ürün fiyatı iş kuralı açısından zorunluysa, eksik fiyatı sıfır kabul etmek hesaplamaları sessizce bozabilir. Benzer biçimde, her hatayı geniş bir try-except KeyError bloğunda yakalamak da yanlış yapılandırılmış verinin fark edilmesini geciktirebilir.

Bu yüzden ilk aşamada amaç hatayı susturmak değil, şu ilişkiyi netleştirmektir: Hata mesajındaki anahtar neydi, sözlükte gerçekte hangi anahtarlar vardı ve bu sözlük hangi noktada üretildi? Bu üç bilgi ortaya çıktıktan sonra köşeli parantez, get(), doğrulama veya try-except arasındaki seçim anlamlı hâle gelir.

Eksik anahtar hangi aşamada ortaya çıkar?

KeyError çoğu zaman yalnızca hatanın görüldüğü erişim satırında ortaya çıkmaz. Hata, sözlüğü oluşturan veya dönüştüren daha önceki bir aşamada başlayabilir; erişim satırı ise bu sorunu görünür hâle getirir. Bu nedenle yalnızca şu satıra bakmak yeterli değildir:

total = order["unit_price"] * order["quantity"]

Bu satırın doğru çalışabilmesi için order sözlüğünün gerçekten unit_price anahtarını taşıması gerekir. Anahtarın neden bulunmadığını anlamak için verinin kaynağına, dönüştürme adımlarına ve veri sözleşmesine geriye doğru bakılmalıdır.

1. Kullanıcı girdisinde alanın boş bırakılması

Bir formdan, komut satırından veya başka bir kullanıcı arayüzünden veri alırken kullanıcı zorunlu alanı boş bırakabilir. Örneğin uygulama unit_price bilgisi bekliyor olabilir; ancak kullanıcı yalnızca ürün adını girerse veri üretim kodu şu sözlüğü oluşturabilir:

product = {
    "name": "Klavye",
    "quantity": 2
}

total = product["unit_price"] * product["quantity"]

Buradaki hata, total hesabında başlamış gibi görünür. Fakat asıl sorun daha önce, kullanıcı girdisinin kontrol edilmeden sözlüğe dönüştürülmesidir. Alan boş bırakıldığında uygulamanın hangi davranışı göstermesi gerektiği önceden belirlenmelidir:

  • unit_price zorunluysa kullanıcıya açık bir doğrulama mesajı gösterilmelidir.
  • Alan gerçekten isteğe bağlıysa eksik değer için iş kuralına uygun bir davranış tanımlanmalıdır.
  • Boş metin, sayısal sıfır veya None doğrudan aynı kabul edilmemelidir.

Örneğin fiyat alanının boş bırakılması, fiyatın sıfır olduğu anlamına gelmez. Boş değer çoğu zaman “kullanıcı veri girmedi” bilgisidir; 0 ise bazı uygulamalarda geçerli bir fiyat olabilir. Bu iki durumu daha sonra birbirinden ayırmak, hatalı toplamların önüne geçer.

2. JSON yanıtında alan adının farklı gelmesi

Bir API veya JSON dosyasından gelen veri, programın beklediği anahtar adlarıyla birebir uyuşmayabilir. Uygulama unit_price beklerken dış kaynaktan price, unitPrice veya başka bir alan adı gelebilir:

response = {
    "name": "Klavye",
    "price": 750,
    "quantity": 2
}

total = response["unit_price"] * response["quantity"]

Bu durumda sözlükte fiyat bilgisi tamamen yok olmayabilir; yalnızca uygulamanın aradığı adla bulunmuyordur. Hata mesajındaki anahtar, bu ayrımı anlamak için önemlidir. KeyError: 'unit_price' görülüyorsa ilk kontrol edilmesi gereken noktalardan biri, verinin gerçek anahtarlarının ne olduğudur.

Teşhis sırasında sözlüğü geçici olarak doğrudan incelemek veya anahtarlarını yazdırmak yararlı olabilir:

print(response.keys())
print(response)

if "unit_price" not in response:
    print("Beklenen unit_price alanı bulunamadı.")

Burada amaç hatayı rastgele bastırmak değil, veri sözleşmesi ile gerçek veri arasındaki farkı görmektir. Alan adları farklı geliyorsa iki yaklaşım düşünülebilir:

  • Veriyi üreten tarafın sözleşmesi düzeltilir.
  • Uygulamaya giren veriyi tek bir iç biçime dönüştüren açık bir eşleme katmanı oluşturulur.

Örneğin dış kaynaktaki price alanı uygulama içinde bilinçli olarak unit_price adına çevrilebilir. Ancak bu dönüşüm rastgele erişim satırlarının içine dağıtılmamalıdır. Dönüşümün tek bir noktada yapılması, sonraki kodun hangi anahtarı kullanacağını netleştirir.

3. Dönüştürme sırasında anahtarın yeniden adlandırılması

Veri akışında sözlük birden fazla fonksiyondan geçebilir. Bu fonksiyonlardan biri anahtar adını değiştirdiğinde, sonraki katman eski adı kullanmaya devam ederse KeyError oluşur.

def normalize_product(data):
    return {
        "name": data["name"],
        "price": data["unit_price"],
        "quantity": data["quantity"]
    }

raw_product = {
    "name": "Klavye",
    "unit_price": 750,
    "quantity": 2
}

product = normalize_product(raw_product)
total = product["unit_price"] * product["quantity"]

Bu örnekte normalize_product() fonksiyonu unit_price anahtarını price olarak yeniden adlandırıyor. Fonksiyonun kendisi çalışsa bile sonraki satır artık sözleşmeye uymuyor. Sorun, erişim satırından önceki dönüştürme işlemindedir.

Böyle bir durumda şu sorular sorulmalıdır:

  • Fonksiyona giren sözlüğün anahtarları nelerdir?
  • Fonksiyondan çıkan sözlüğün anahtarları nelerdir?
  • Sonraki fonksiyon hangi alan adlarını beklemektedir?
  • Yeniden adlandırma bilinçli bir karar mı, yoksa yanlışlıkla mı yapılmıştır?

Özellikle price ve unit_price gibi anlamca yakın alan adları, hatayı zorlaştırabilir. İki isim de fiyatı çağrıştırır; fakat birinin ürünün tek birimi için, diğerinin farklı bir fiyat türü için kullanılması mümkündür. Anahtar adı yalnızca yazım ayrıntısı değil, veri sözleşmesinin parçasıdır.

4. Veri üretim kodunun zorunlu alanı hiç eklememesi

Bazen eksik anahtarın nedeni dış veri veya kullanıcı hatası değildir. Sözlüğü oluşturan kod, zorunlu alanı hiç eklememiş olabilir:

def create_order(name, quantity):
    return {
        "name": name,
        "quantity": quantity
    }

order = create_order("Klavye", 2)
print(order["unit_price"])

Bu örnekte create_order() fonksiyonu unit_price parametresini almıyor ve dönen sözlüğe bu anahtarı eklemiyor. Sonraki kodun her siparişte fiyat beklemesi, üretici fonksiyonun veri sözleşmesini eksik tanımladığını gösterir.

Kalıcı çözüm, erişim satırını get() ile değiştirmek olmayabilir. Eğer fiyat olmadan siparişin anlamlı bir şekilde işlenmesi mümkün değilse, fiyatın veri üretim aşamasında zorunlu hâle getirilmesi gerekir:

def create_order(name, quantity, unit_price):
    if unit_price is None:
        raise ValueError("unit_price zorunludur")

    return {
        "name": name,
        "quantity": quantity,
        "unit_price": unit_price
    }

Bu yaklaşım, sorunu daha erken ve daha anlaşılır bir noktada yakalar. Böylece uygulama ileride anlamsız bir toplam üretmek yerine, eksik zorunlu veriyi açıkça bildirir.

Önce veri sözleşmesini belirleyin

Bir anahtarın eksik olması karşısında hangi yöntemin seçileceğine karar vermeden önce alanın rolü belirlenmelidir. unit_price ve discount_code bu ayrımı göstermek için iyi bir örnektir:

  • unit_price: Toplam hesaplama gibi temel bir işleme ihtiyaç duyuluyorsa zorunlu alandır.
  • discount_code: Kullanıcı indirim kodu girmediğinde de sipariş işlenebiliyorsa isteğe bağlı alandır.

Zorunlu alan için eksikliği saklamak genellikle hatayı daha ileri bir aşamaya taşır. İsteğe bağlı alan içinse eksiklik normal bir durum olabilir. Örneğin indirim kodu bulunmadığında uygulama indirimsiz devam edebilir; ancak fiyat bulunmadığında toplam hesabı güvenilir biçimde yapılamaz.

Bu nedenle şu soruya cevap verilmeden get() veya try-except eklenmemelidir: Bu anahtarın bulunmaması geçerli bir iş durumu mu, yoksa verinin bozuk olduğunu mu gösteriyor?

Köşeli parantez, get(), doğrulama ve try-except nasıl karşılaştırılır?

Köşeli parantez, get(), doğrulama ve try-except nasıl karşılaştırılır?

Python sözlüğündeki eksik anahtarı ele almak için tek bir “doğru” yöntem yoktur. Seçim, alanın zorunlu olup olmamasına, hatanın ne kadar erken görünmesi gerektiğine ve uygulamanın bu durumu nasıl yönetmesi gerektiğine bağlıdır. Aşağıdaki karşılaştırma temel farkları özetler:

Yöntem Anahtar eksikse davranış Hata görünürlüğü Uygun kullanım Temel risk
Köşeli parantez: data["key"] KeyError oluşur Yüksek; sorun hemen görünür Zorunlu ve sözleşmesi kesin alanlar Eksiklik beklenen bir durumsa uygulama durabilir
get(): data.get("key") None veya verilen varsayılan değer döner Orta veya düşük; hata sessizleşebilir Gerçekten isteğe bağlı alanlar Yanlış varsayılan değer bozuk sonucu gizleyebilir
Açık veri doğrulama Belirlenen kurala göre hata veya açıklayıcı sonuç üretilir Yüksek; iş kuralı görünürdür Form, JSON ve dış veri kontrolü Kurallar eksik tanımlanırsa tutarsızlık oluşabilir
Dar kapsamlı try-except KeyError Hata yakalanır ve kontrollü işlem yapılır Doğru kullanılırsa yüksek Beklenen erişim hatasına özel yönetim Geniş blokta kullanılırsa gerçek kod hatalarını saklayabilir

Köşeli parantez: zorunlu alanlarda açık hata

data["unit_price"] kullanımı, alanın mutlaka bulunması gerektiğini anlatır. Anahtar yoksa Python’un KeyError üretmesi, geliştiriciye veri sözleşmesinin bozulduğunu bildirir.

order = {
    "unit_price": 750,
    "quantity": 2
}

total = order["unit_price"] * order["quantity"]
print(total)

Bu kodun beklenen çıktısı 1500 olur. unit_price eksik olduğunda ise toplamı tahmin etmek yerine hata vermek daha güvenlidir. Özellikle ödeme, stok, puan veya raporlama gibi sonuçların doğru olması gereken işlemlerde eksik zorunlu alanı gizlemek yerine erken fark etmek gerekir.

get(): yalnızca isteğe bağlı alanlarda

get(), anahtar bulunmadığında sözlüğün hata vermeden devam etmesini sağlar:

order = {
    "unit_price": 750,
    "quantity": 2
}

discount_code = order.get("discount_code")
print(discount_code)

Burada discount_code alanının bulunmaması geçerli bir durum olabilir. Kullanıcı indirim kodu girmediyse None döner ve uygulama indirimsiz sipariş akışına devam edebilir.

Ancak zorunlu bir alan için rastgele varsayılan değer vermek risklidir:

order = {
    "quantity": 2
}

total = order.get("unit_price", 0) * order["quantity"]
print(total)

Bu kod 0 yazdırır. Teknik olarak hata vermemiştir; fakat fiyatın eksik olması, ürünün gerçekten ücretsiz olduğu anlamına gelmez. Uygulama bu sonucu kaydeder, raporlar veya kullanıcıya gösterirse veri hatası sessizce gerçek bir sonuca dönüşür. Bu yüzden get("price", 0) gibi varsayılanlar ancak iş kuralı açıkça “alan yoksa değer sıfır kabul edilir” diyorsa kullanılmalıdır.

None, boş metin ve sıfır aynı anlama gelmez

Eksik veriyi kontrol ederken None, boş metin ve 0 değerlerini birbirine karıştırmamak gerekir:

  • None, çoğu akışta değerin verilmediğini veya henüz bilinmediğini ifade eder.
  • "", alanın metin olarak boş gönderildiğini gösterir.
  • 0, sayısal olarak sıfır değeridir ve bazı alanlarda geçerli olabilir.

Örneğin quantity için sıfır çoğu sipariş akışında kabul edilmemesi gereken bir değer olabilir. Buna karşılık discount_amount için sıfır, indirim uygulanmadığını gösterebilir. Dolayısıyla yalnızca “değer var mı?” kontrolü değil, alanın türü ve iş anlamı da değerlendirilmelidir.

Açık veri doğrulama: iş kuralını görünür kılma

Veri doğrulama, anahtarın var olup olmadığını kontrol etmekle kalmaz; değerin kullanılabilir olup olmadığını da belirler:

def calculate_total(order):
    if "unit_price" not in order:
        raise ValueError("unit_price alanı zorunludur")

    if order["unit_price"] is None:
        raise ValueError("unit_price boş bırakılamaz")

    if order["quantity"] <= 0:
        raise ValueError("quantity sıfırdan büyük olmalıdır")

    return order["unit_price"] * order["quantity"]

Bu örnekte unit_price eksikse KeyError yerine alanın iş kuralına daha yakın bir ValueError mesajı üretilir. Böyle bir doğrulama, dışarıdan gelen JSON verisini uygulamanın iç mantığına almadan önce yapılabilir.

Doğrulama özellikle şu durumlarda değerlidir:

  • Dış kaynaktan gelen verinin biçimi garanti edilemiyorsa
  • Kullanıcı girdisi doğrudan hesaplamaya katılıyorsa
  • Birden fazla fonksiyon aynı sözlük yapısını kullanıyorsa
  • Hatanın kullanıcıya anlaşılır bir mesajla gösterilmesi gerekiyorsa

Dar kapsamlı try-except: kontrollü hata yönetimi

try-except KeyError, eksik anahtarın beklenen bir çalışma durumu olduğu ve uygulamanın bu duruma özel bir tepki vermesi gerektiği zaman kullanılabilir:

try:
    unit_price = order["unit_price"]
except KeyError:
    print("Sipariş fiyat bilgisi olmadan işlenemez.")
else:
    total = unit_price * order["quantity"]
    print(total)

Burada try bloğu yalnızca riskli sözlük erişimini kapsar. Tüm hesaplama, dosya işlemleri veya program akışı geniş bir try bloğuna alınmadığı için hangi işlemin hata verdiği anlaşılabilir kalır.

Geniş kapsamlı kullanım ise teşhisi zorlaştırabilir:

try:
    unit_price = order["unit_price"]
    total = unit_price * order["quantity"]
    save_order(order)
    send_notification(order)
except KeyError:
    print("Bir alan eksik.")

Bu blokta hangi sözlük erişiminin sorun çıkardığı belirsizleşir. Ayrıca save_order() veya send_notification() içinde oluşan başka bir KeyError da aynı mesajla karşılanabilir. Bu nedenle try-except kullanılıyorsa kapsam mümkün olduğunca dar tutulmalı ve hata mesajı hangi alanın eksik olduğunu açıklamalıdır.

Pratik karar kuralı şöyledir: zorunlu alanlarda köşeli parantez ve açık doğrulama, isteğe bağlı alanlarda bilinçli bir varsayımla get(), beklenen istisnayı kullanıcıya veya üst katmana çevirmek gerektiğinde dar kapsamlı try-except tercih edilir. Yöntem, KeyError’u susturmak için değil, veri sözleşmesini doğru ifade etmek için seçilmelidir.

price ve unit_price uyuşmazlığı kod üzerinde nasıl düzeltilir?

Bir ürün verisi unit_price anahtarını taşıdığı hâlde kod içinde price anahtarına erişmeye çalışıyorsa sorun Python’un sözlük yapısında değil, beklenen anahtar ile gerçek veri sözleşmesinin uyuşmamasındadır. Önce hatayı kontrollü biçimde görünür kılmak, ardından erişimi veride gerçekten bulunan anahtara göre düzeltmek gerekir.

Hatalı erişimi görünür kılan örnek

product = {
    "name": "Kablosuz Klavye",
    "unit_price": 799.90,
    "quantity": 2
}

try:
    total = product["price"] * product["quantity"]
    print(f"Ürün toplamı: {total:.2f} TL")
except KeyError as error:
    print(f"Eksik anahtar: {error}")

Bu kodun beklenen çıktısı şöyledir:

Eksik anahtar: 'price'

Teşhis süreci satır satır incelendiğinde neden-sonuç ilişkisi açıkça görülür:

  • product = { satırı, ürün bilgilerini taşıyan sözlüğü oluşturur. Kodun geri kalanında hangi alanlara erişilebileceği bu sözlükteki anahtarlarla sınırlıdır.
  • "name": "Kablosuz Klavye" satırında ürünün adı name anahtarı altında tutulur. Bu anahtar örnekte doğrudan kullanılmasa da sözlüğün gerçek yapısının bir parçasıdır.
  • "unit_price": 799.90 satırı, ürünün birim fiyatının unit_price adıyla üretildiğini gösterir. Burada price adında başka bir anahtar bulunmaz.
  • "quantity": 2 satırı, toplam hesabında kullanılacak ürün miktarını tanımlar.
  • try: satırı, hata üretme ihtimali olan erişimi dar bir alanda denemek için kullanılır. Bu blok, programın tamamını sarmalamaz.
  • product["price"] ifadesi hatanın doğduğu noktadır. Köşeli parantez, tam olarak price anahtarını arar.
  • Sözlükte price bulunmadığı için Python KeyError üretir. Hata mesajındaki 'price' bölümü, eksik olduğu düşünülen anahtarın adını verir.
  • except KeyError as error: satırı yalnızca bu sözlük anahtarı hatasını yakalar. Böylece örneğin matematiksel bir hata veya farklı bir programlama hatası sessizce gizlenmez.
  • print(f"Eksik anahtar: {error}") satırı, hatanın hangi anahtar üzerinde oluştuğunu kullanıcıya gösterir. Çıktıdaki tırnak işaretleri, hata nesnesinin metinsel gösteriminden gelir.

Bu ilk örnek sorunu çözmez; fakat hatanın kaynağını güvenli biçimde görünür kılar. Özellikle dış bir JSON yanıtı, dosya veya API verisiyle çalışırken önce hangi anahtarın istendiğini ve veride hangi anahtarların bulunduğunu ayırmak önemlidir. Hata mesajı 'price' anahtarını işaret ederken veri sözlüğünde unit_price bulunuyorsa, ilk inceleme noktası erişim satırıdır.

Veri sözleşmesine uygun düzeltilmiş örnek

product = {
    "name": "Kablosuz Klavye",
    "unit_price": 799.90,
    "quantity": 2
}

required_keys = ("name", "unit_price", "quantity")
missing_keys = [key for key in required_keys if key not in product]

if missing_keys:
    raise ValueError(f"Eksik zorunlu alanlar: {missing_keys}")

total = product["unit_price"] * product["quantity"]
print(f"{product['name']} toplamı: {total:.2f} TL")

Bu kodun beklenen çıktısı şöyledir:

Kablosuz Klavye toplamı: 1599.80 TL

Düzeltilmiş örnekte iki önemli karar vardır. Birincisi, ürün toplamı için gerekli alanlar açıkça required_keys içinde tanımlanır. İkincisi, hesaplama product["unit_price"] üzerinden yapılır; yani erişim, verinin gerçekten kullandığı isimle eşleşir.

  • required_keys = ("name", "unit_price", "quantity") satırı, bu işlem için zorunlu kabul edilen alanları belirler. Böylece kodun hangi veri sözleşmesine göre çalıştığı belirsiz kalmaz.
  • missing_keys = [...] satırı, beklenen her anahtarı sözlük içinde kontrol eder. Anahtar yoksa listeye eklenir.
  • if missing_keys: satırı, eksik alan listesinin boş olup olmadığını kontrol eder. Liste boş değilse ürün verisi hesaplama için hazır değildir.
  • raise ValueError(...) satırı, eksik zorunlu alanları açık bir doğrulama hatası olarak bildirir. Bu durumda sessizce sıfır fiyat kullanmak yerine veri kalitesi sorunu görünür tutulur.
  • total = product["unit_price"] * product["quantity"] satırı, veri sözleşmesindeki doğru anahtarı kullanır. Birim fiyat ile miktar çarpılarak toplam hesaplanır.
  • Son print satırı ürün adını ve toplam tutarı ekrana yazdırır. :.2f biçimlendirmesi, sonucu iki ondalık basamakla gösterir.

Alternatif olarak veri kaynağının sözleşmesini değiştirmek de mümkündür. Örneğin bütün uygulama katmanları price adını bekliyorsa, dönüşüm aşamasında unit_price alanı bilinçli biçimde price alanına dönüştürülebilir. Ancak bu dönüşüm rastgele bir satırda yapılmamalıdır. Veri dışarıdan hangi adla geliyor, uygulamanın iç modeli hangi adı kullanıyor ve çıktı hangi adı gerektiriyor soruları birlikte değerlendirilmelidir.

Gerçek bir projede bu tür anahtar eşleşmelerini ve sözlük erişimlerini birebir Python dersleri kapsamında örnekler üzerinden çalışmak, yalnızca hata mesajını okumaktan daha ileri bir teşhis alışkanlığı kazandırabilir.

Gerçek anahtarları incelemek neden önemlidir?

Hata mesajı yalnızca kodun aradığı anahtarı söyler. Verinin hangi anahtarları taşıdığını görmek için sözlüğün kendisi veya anahtar listesi incelenmelidir:

print(product.keys())
print(list(product))

Bu iki ifade benzer bir amaçla kullanılır: sözlükteki gerçek anahtar adlarını gözlemlemek. Çıktıda unit_price görülüyor, price görülmüyorsa sorun hesaplama formülünden önce isim eşleşmesindedir. Büyük-küçük harf, alt çizgi ve kelime sırası da anahtarın parçasıdır. unit_price, unitPrice ve price Python açısından üç farklı anahtardır.

Bu nedenle düzeltme yalnızca product["price"] ifadesini product["unit_price"] olarak değiştirmekten ibaret olmayabilir. Veri kaynağında alanın bazen price, bazen unit_price olarak gelmesi söz konusuysa önce veri standardı belirlenmeli, ardından dönüşüm veya doğrulama tek bir noktada yapılmalıdır. Aynı uyuşmazlığı programın farklı bölümlerinde ayrı ayrı düzeltmeye çalışmak, ileride yeni KeyError hataları üretir.

Doğru çözüm yaklaşımı hangi durumda seçilmelidir?

KeyError için tek bir evrensel çözüm yoktur. Doğru yöntem, anahtarın iş mantığındaki rolüne göre seçilir: Alan zorunlu mu, isteğe bağlı mı, eksik gelmesi beklenen bir durum mu, yoksa veri kaynağında yanlış bir ad mı üretiliyor? Bu sorular yanıtlanmadan her erişimi get() veya try-except ile sarmalamak, gerçek problemi gizleyebilir.

Alan zorunluysa doğrudan erişim veya ön doğrulama kullanın

Bir alan olmadan işlem anlamlı biçimde devam edemiyorsa doğrudan köşeli parantez kullanmak uygundur:

order = {
    "order_id": 1042,
    "customer_id": 87
}

order_id = order["order_id"]
customer_id = order["customer_id"]

Burada order_id veya customer_id yoksa işlemin devam etmesi güvenilir değildir. Hatanın erken oluşması, eksik verinin daha sonra yanlış bir sonuç üretmesinden daha açıklayıcı olabilir. Dış veriyi işleme alırken ise hesaplamadan önce zorunlu alanları topluca doğrulamak daha düzenlidir.

Örneğin bir ürünün toplamını hesaplamak için unit_price ve quantity zorunluysa bu alanlardan biri eksikken toplamı sıfır kabul etmek doğru değildir. Sıfır, ürünün gerçekten ücretsiz olduğu anlamına gelebilir; eksik veri ile gerçek sıfır değerini birbirine karıştırır.

Alan isteğe bağlıysa anlamlı bir varsayılanla get() kullanın

Bir alanın eksik olması normal ve iş kuralı açısından kabul edilebilir durumdaysa get() tercih edilebilir:

product = {
    "name": "USB Kablo",
    "quantity": 3
}

note = product.get("note", "Not girilmedi")
print(note)

Burada note alanı ürünün hesaplanması için gerekli değildir. Eksik olduğunda "Not girilmedi" ifadesi kullanıcı açısından anlamlı bir varsayılandır. Ancak varsayılan değer, alanın anlamını değiştirmemelidir. Eksik kargo adresini "" ile doldurmak, eksik ödeme durumunu False saymak veya bilinmeyen fiyatı 0 kabul etmek çoğu senaryoda risklidir.

Hata beklenen ve yerel olarak yönetilen bir durumsa dar kapsamlı try-except kullanın

Bazen eksik anahtar, programın olağan akışında karşılaşılabilecek bir durumdur. Bu durumda try-except KeyError kullanılabilir; fakat blok yalnızca ilgili erişimi kapsamalıdır:

settings = {
    "theme": "dark"
}

try:
    language = settings["language"]
except KeyError:
    language = "tr"

print(f"Dil: {language}")

Bu örnekte language ayarının bulunmaması, uygulamanın varsayılan dili kullanmasıyla yönetilebilen yerel bir durumdur. Buna karşılık bütün dosya okuma, veri dönüştürme ve hesaplama sürecini tek bir geniş try bloğuna almak doğru değildir.

Geniş try-except blokları teşhisi nasıl zorlaştırır?

Aşağıdaki yaklaşım ilk bakışta programı çalışır tutuyor gibi görünebilir:

try:
    name = product["name"]
    total = product["unit_price"] * product["quantity"]
    save_to_database(name, total)
except KeyError:
    print("Ürün verisi eksik")

Ancak burada hangi satırın KeyError ürettiği belirsizdir. Sorun product sözlüğündeki bir anahtar olabilir; save_to_database içinde kullanılan başka bir sözlükte de ortaya çıkabilir. Üstelik hata yakalandığında yalnızca Ürün verisi eksik yazdırılır ve gerçek anahtar adı kaybolur.

Daha güvenli yaklaşım, kritik erişimleri ayrı ayrı doğrulamak veya yalnızca hatanın beklenebileceği ifadeyi sarmalamaktır. Hata yönetimi programı susturmak için değil, hatanın anlamlı biçimde ele alınması için kullanılmalıdır.

Anahtar adı yanlış üretiliyorsa kaynağı düzeltin

Veri kaynağı sürekli olarak unit_price gönderiyor, fakat uygulamanın dönüştürme katmanı her nesneyi price alanına göre oluşturuyorsa sorun erişim satırından daha önce başlamış olabilir. Bu durumda her kullanım noktasında ayrı ayrı kontrol eklemek yerine, veri kaynağı ile uygulama modeli arasındaki dönüşüm düzeltilmelidir.

Örneğin şu iki yapıdan hangisinin kullanılacağı proje sözleşmesine göre belirlenmelidir:

  • Dış kaynaktaki unit_price adı uygulamada da korunur ve bütün erişimler bu adla yapılır.
  • Dış kaynakta unit_price alınır, tek bir dönüştürme noktasında uygulamanın standart alanı olan price oluşturulur.

İki yaklaşımın aynı projede gelişigüzel karıştırılması sorun çıkarır. Bir fonksiyon price, başka bir fonksiyon unit_price beklerse veri sözleşmesi bulanıklaşır. Bu yüzden anahtar adlarını, zorunlu alanları ve türleri dokümante etmek; JSON veya sözlük uygulamaya girerken doğrulamak yararlıdır.

Karar çerçevesi

Durum Uygun yaklaşım Neden
Alan olmadan işlem yapılamıyorsa Doğrudan erişim veya ön doğrulama Eksik veriyi gizlemeden erken hata üretir.
Alan isteğe bağlıysa Anlamlı varsayılanla get() Eksiklik normal akışta yönetilir.
Eksik anahtar beklenen yerel bir durumsa Dar kapsamlı try-except KeyError Yalnızca ilgili hata kontrollü biçimde ele alınır.
Anahtar adı yanlış üretiliyorsa Kaynağı veya dönüştürme aşamasını düzeltme Aynı hatanın farklı noktalarda tekrarlanmasını önler.

Bu kararları gerçek kod akışında uygulamak için canlı Python eğitimi gibi etkileşimli öğrenme ortamlarında hata mesajını, veri yapısını ve çözüm seçimini birlikte değerlendirmek faydalı olabilir. Buradaki amaç belirli bir yöntemi ezberlemek değil, verinin iş kuralındaki rolüne göre yöntem seçmektir.

Yeniden kullanılabilir kontrol listesi

  1. Hata mesajındaki anahtar adını aynen okuyun. KeyError: 'price' ifadesinde aranan anahtar price adıdır.
  2. Hatanın oluştuğu satırı belirleyin. Sorun gerçekten sözlük erişiminde mi, yoksa erişimden sonra çağrılan başka bir fonksiyonda mı kontrol edin.
  3. Verinin gerçek anahtarlarını inceleyin. keys() veya list(veri) çıktısında price ile unit_price ayrımını doğrulayın.
  4. Alanı zorunlu veya isteğe bağlı olarak sınıflandırın.
  5. Alan zorunluysa eksik veriyi varsayılanla geçiştirmeyin; doğrudan erişim veya açık doğrulama kullanın.
  6. Alan isteğe bağlıysa seçtiğiniz varsayılanın iş anlamını kontrol edin. Varsayılan değer, eksikliği geçerli bir değer gibi göstermemelidir.
  7. Hata beklenen bir akış durumuysa yalnızca ilgili erişimi dar bir try-except KeyError bloğunda yönetin.
  8. Anahtar adı veri kaynağında veya dönüştürme aşamasında yanlış üretiliyorsa düzeltmeyi kaynağa yakın noktada yapın.
  9. Çözümden sonra aynı veri sözleşmesini kullanan diğer fonksiyonları da kontrol edin.

Sık Sorulan Sorular

Python KeyError hatasında eksik anahtarı nasıl bulabilirim?

Önce hata mesajındaki anahtar adını okuyun; örneğin KeyError: 'price', kodun price anahtarını aradığını gösterir. Ardından hatanın oluştuğu sözlüğün gerçek anahtarlarını print(veri.keys()) veya print(list(veri)) ile inceleyin. Böylece anahtarın gerçekten eksik mi olduğunu, yoksa unit_price gibi farklı bir adla mı geldiğini görebilirsiniz.

KeyError almamak için her zaman get() kullanmak doğru mudur?

Hayır. Alan zorunluysa get() kullanarak eksik veriyi fark edilmeden varsayılanla değiştirmek yanlış sonuç üretebilir. get(), yalnızca alanın isteğe bağlı olduğu ve seçilen varsayılan değerin iş açısından gerçekten anlamlı olduğu durumlarda kullanılmalıdır.

get() ile varsayılan değer vermek hangi durumlarda risklidir?

Varsayılan değer, eksik veriyi geçerli bir veri gibi gösteriyorsa risklidir. Bilinmeyen fiyatı 0, eksik miktarı 0 veya eksik yetki bilgisini False kabul etmek iş mantığını bozabilir. Özellikle finans, sipariş ve yetkilendirme gibi alanlarda eksik veriyi açıkça doğrulamak daha güvenlidir.

try-except KeyError hatasını ne zaman gizler?

Geniş bir kod bölümünü saran ve hata mesajının ayrıntısını kaybeden try-except blokları hatayı gizleyebilir. Birden fazla sözlük erişimi aynı blokta bulunuyorsa hangi anahtarın eksik olduğu anlaşılamaz. Bu nedenle try bloğunu dar tutun, mümkünse hata nesnesindeki anahtar adını kaybetmeyin ve beklenmeyen veri sorunlarını sessizce geçiştirmeyin.

JSON verisindeki alan adı değiştiğinde sorun kodda mı veri kaynağında mı düzeltilmelidir?

Önce veri sözleşmesinin hangi alan adını tanımladığını belirlemek gerekir. JSON kaynağı bilinçli olarak unit_price gönderiyor ve uygulama bunu desteklemiyorsa dönüştürme katmanı veya uygulamadaki erişim düzeltilmelidir. Kaynak, daha önce tanımlanmış sözleşmeye aykırı biçimde alan adını değiştiriyorsa kaynak tarafı da incelenmelidir. En sağlıklı yaklaşım, iki adı kodun her yerine dağıtmak yerine tek bir standart belirleyip girişte doğrulamaktır.

Bir KeyError hatasını kalıcı olarak çözmenin yolu, yalnızca hatayı susturmak değil; beklenen anahtar ile üretilen verinin aynı sözleşmeye dayanmasını sağlamaktır.

Bu içerik aradığın cevabı verdi mi?
Yanıtın, hangi yazıları geliştirmemiz gerektiğini anlamamıza yardımcı olur.
Bu içeriğin üretilmesinde yapay zeka araçlarından destek alınmıştır.

Bu konudan sonra ne okuyabilirsin?

Tüm yazılar

İlgili Eğitimler

Berk Keskin — Yazılım Geliştirici ve Eğitmen
Yazar

Berk Keskin Kimdir?

Yazılıma 12 yaşında başladı; bugün öğrencinin seviyesine ve hedefine göre şekillenen sürdürülebilir öğrenme sistemleri tasarlıyor. 500'den fazla kişiye ezber değil, düşünerek kod yazmayı öğretti — Berk Akademi'de izlemeye değil üretmeye dayalı öğrenme kültürünü o kuruyor.

WhatsApp Hemen Ara