Genel fikir birliği “istisna kullanmayın!” çoğunlukla diğer dillerden geliyor ve hatta bazen modası geçmiş.
C ++ 'da, “yığın gevşemesi” nedeniyle istisna atmak çok masraflıdır . Her yerel değişken bildirimi with
Python'daki bir ifadeye benzer ve bu değişken içindeki nesne yıkıcıları çalıştırabilir. Bu yıkıcılar bir istisna atıldığında değil, aynı zamanda bir işlevden döndüğünde yürütülür. Bu “RAII deyimi” ayrılmaz bir dil özelliğidir ve sağlam, doğru kod yazmak için çok önemlidir - bu nedenle RAII'nin ucuz istisnalara karşı C ++, RAII'ye karar verdiği bir ödünleşmedir.
Erken C ++ 'da, çok sayıda kod istisna açısından güvenli bir şekilde yazılmamıştır: aslında RAII kullanmıyorsanız, bellek ve diğer kaynaklara sızmak kolaydır. Dolayısıyla istisnalar atmak bu kodu yanlış yapar. C ++ standart kitaplığı bile istisnalar kullandığından artık mantıklı değil: İstisnalar yokmuş gibi davranamazsınız. Ancak, C kodu C ++ ile birleştirilirken istisnalar hala bir sorundur.
Java'da, her istisnanın ilişkili bir yığın izlemesi vardır. Hata izleme hata ayıklama sırasında yığın izlemesi çok değerlidir, ancak kural dışı durum hiçbir zaman yazdırılmadığında, örneğin yalnızca kontrol akışı için kullanıldığı için harcanan çabadır.
Dolayısıyla bu dillerde istisnalar kontrol akışı olarak kullanılmak için “çok pahalı” dır. Python'da bu daha az sorun ve istisnalar çok daha ucuz. Buna ek olarak, Python dili, diğer kontrol akışı yapılarına kıyasla istisnaların maliyetini farkedilmez hale getiren bir ek yükten muzdariptir: örneğin, açık bir üyelik testi ile bir dikte girişinin olup olmadığını kontrol etmek if key in the_dict: ...
genellikle girişe erişmek kadar hızlıdır the_dict[key]; ...
ve bir KeyError alın. Bazı ayrılmaz dil özellikleri (örneğin, jeneratörler) istisnalar açısından tasarlanmıştır.
Bu nedenle, Python'daki istisnalardan özellikle kaçınmak için teknik bir neden olmasa da, dönüş değerleri yerine bunları kullanmanız gerekip gerekmediği sorusu hala var. İstisnalar dışında tasarım düzeyinde sorunlar:
hiç belli değiller. Bir işleve kolayca bakamaz ve hangi istisnaları atabileceğini göremezsiniz, böylece ne yakalayacağınızı her zaman bilemezsiniz. Dönüş değeri daha iyi tanımlanma eğilimindedir.
istisnalar, kodunuzu karmaşıklaştıran yerel olmayan kontrol akışıdır. Bir istisna attığınızda, kontrol akışının nereye devam edeceğini bilmiyorsunuz. Hemen ele alınamayan hatalar için, bu muhtemelen iyi bir fikirdir, arayan kişiye bir durumu bildirirken bu tamamen gereksizdir.
Python kültürü genellikle istisnalar lehine eğimlidir, ancak denize girmek kolaydır. list_contains(the_list, item)
Listenin o öğeye eşit bir öğe içerip içermediğini kontrol eden bir işlev düşünün . Sonuç kesinlikle sinir bozucu istisnalar aracılığıyla iletilirse, çünkü bunu şöyle çağırmalıyız:
try:
list_contains(invited_guests, person_at_door)
except Found:
print("Oh, hello {}!".format(person_at_door))
except NotFound:
print("Who are you?")
Bir boole geri dönmek çok daha açık olurdu:
if list_contains(invited_guests, person_at_door):
print("Oh, hello {}!".format(person_at_door))
else:
print("Who are you?")
Fonksiyonun zaten bir değer döndürmesi gerekiyorsa, özel koşullar için özel bir değer döndürmek oldukça hataya açıktır, çünkü insanlar bu değeri kontrol etmeyi unutacaktır (muhtemelen C'deki sorunların 1 / 3'ünün nedeni budur). Bir istisna genellikle daha doğrudur.
Buna iyi bir örnek, samanlık dizesindeki dizenin pos = find_string(haystack, needle)
ilk oluşumunu needle
araştıran ve başlangıç konumunu döndüren bir işlevdir . Peki samanlık ipi iğne ipi içermiyorsa ne olur?
C'nin çözümü ve Python tarafından taklit edilmesi özel bir değer döndürmektir. C'de bu bir boş gösterici, Python'da bu -1
. Konum, özellikle -1
Python'daki geçerli bir dizin gibi , denetim olmadan dizgi dizini olarak kullanıldığında şaşırtıcı sonuçlar doğuracaktır . C olarak, NULL işaretçiniz size en azından bir segfault verecektir.
PHP'de, farklı türde özel bir değer döndürülür: FALSE
tamsayı yerine boole . Anlaşıldığı gibi, bu, dilin örtülü dönüştürme kuralları nedeniyle daha iyi değildir (ancak Python'da booleanların ints olarak kullanılabileceğini unutmayın!). Tutarlı bir tür döndürmeyen işlevler genellikle çok kafa karıştırıcı kabul edilir.
Daha sağlam bir varyant, dize bulunamadığında bir istisna atmak olurdu, bu da normal kontrol akışı sırasında özel bir değeri yanlışlıkla sıradan bir değer yerine kullanmanın imkansız olmasını sağlar:
try:
pos = find_string(haystack, needle)
do_something_with(pos)
except NotFound:
...
Alternatif olarak, her zaman doğrudan kullanılamayan ancak ilk olarak ambalajın açılması gereken bir türün döndürülmesi kullanılabilir, örneğin booleanın bir istisnanın meydana gelip gelmediğini veya sonucun kullanılabilir olup olmadığını gösteren bir sonuç-bool demet. Sonra:
pos, ok = find_string(haystack, needle)
if not ok:
...
do_something_with(pos)
Bu sizi derhal problemlerle başa çıkmaya zorlar, ancak çok çabuk sinir bozucu olur. Ayrıca kolayca zincirleme fonksiyonunu engeller. Artık her işlev çağrısı için üç satır kod gerekir. Golang, bu rahatsızlığın güvenliğe değer olduğunu düşünen bir dildir.
Özetlemek gerekirse, istisnalar tamamen problemsiz değildir ve özellikle “normal” bir dönüş değerinin yerini aldıklarında kesinlikle aşırı kullanılabilir. Ancak, özel koşullara işaret etmek için kullanıldığında (sadece hatalar değil), istisnalar temiz, sezgisel, kullanımı kolay ve kötüye kullanımı zor API'ler geliştirmenize yardımcı olabilir.