2.26. Hata yükseltme

Bir işlev, bir istisnayı yükselterek çağırana bir sorun sinyali verebilir. Anahtar kelime raise‘dir:

def square_root(x):
    if x < 0:
        raise ValueError("square_root expects a non-negative number")
    return x ** 0.5

square_root(-1) çağrısı raise satırında durur, işlevden çıkar ve çağırıcıda eşleşen bir except arar. Hiçbir çağırıcı onu yakalamazsa, betik bir geri izlemeyle sona erer.

2.26.1. Bir sentinel döndürmek yerine neden yükseltmeli

“Kötü girdi”yi bildirmenin iki yolu:

# signal with a sentinel
def square_root_or_none(x):
    if x < 0:
        return None
    return x ** 0.5

# raise an exception
def square_root(x):
    if x < 0:
        raise ValueError("...")
    return x ** 0.5

İstisna biçimi genellikle daha iyidir:

  • Çağırıcı, hata durumunu kasıtlı olarak ele almak zorundadır – ya bir try ile ya da istisnanın yayılmasına izin vererek. Sentinel’lar unutulması kolaydır ve normal bir sonuçla karıştırılması kolaydır.

  • Hata mesajı istisnayla birlikte taşınır; sentinel yaklaşımı tanılamayı başka bir yere eklemek zorundadır.

  • Ele alınmayan bir istisnadaki varsayılan davranış, sorunlu çağrıyı işaret eden bir geri izlemeyle birlikte gürültülü bir çökmedir. Sessiz None dönüşleri daha sonra ince hatalara dönüşür.

Sentinel’lara yalnızca “bulunamadı” rutin, istisnai olmayan bir sonuç olduğunda başvurun – dict.get(), tam olarak aramaların bazen başarısız olmasının beklenmesi nedeniyle eksik bir anahtarda None döndürür.

2.26.2. Özel istisna sınıfları

Çağırıcının yerleşik hatalardan ayırmak isteyebileceği bir sorunu yükseltmek için, Exception‘ın bir alt sınıfını tanımlayın:

class ConfigError(Exception):
    pass

def load_config(path):
    try:
        f = open(path)
    except OSError as e:
        raise ConfigError("missing config file: " + path)

try:
    load_config("settings.json")
except ConfigError as e:
    print("startup failed:", e)

Boş class gövdesi sorun değil – önemli olan adın kendisidir, çünkü çağırıcılar sınıfa göre yakalar. Bir çağırıcı tüm aileyi tek bir blokta yakalamak isteyebilirse, ilgili hataları ortak bir tabanın altında gruplayın.

2.26.2.1. Yeniden yükseltme

Bir except bloğu içindeki çıplak bir raise, geçerli istisnayı yeniden yükseltir, böylece bir sonraki işleyiciye yayılır:

try:
    do_work()
except Exception as e:
    log(e)
    raise        # let it keep going

Bu, bir işlevin bir hatayı gerçekten ele almadan gözlemlemek (günlüğe kaydetmek, saymak, kısmi bir değişikliği geri almak) istediğinde doğru şekildir.

2.26.3. Ne zaman yakalanmalı ve ne zaman yayılmalı

Yararlı bir pratik kural:

  • Bir istisnayı, anlamlı bir şekilde kurtarılabilecek düzeyde yakalayın – bir varsayılan koyun, yeniden deneyin, kötü girdiyi atlayın.

  • Çökmek dışında yapılacak yararlı bir şey olmadığında veya nasıl kurtarılacağını bilen katman üstteki katman olduğunda yayılmasına izin verin.

Çağrı yığınının ortasında her hatayı yutan ve sessizce dönen bir işlev, hataları izlenemez hale getirir. İstisnaların, gerçekten bir planı olan koda ulaşana kadar yol almasına izin vermeyi tercih edin.