Bir şeyi 'denemek' ve istisnayı yakalamak veya bir istisnayı önlemek için ilk önce mümkün olup olmadığını test etmek daha mı iyi?


139

Bir ifşeyin geçerli olduğunu test etmeli miyim yoksa sadece trybunu yapmalı ve istisnayı yakalamalıyım?

  • Tek yönün tercih edildiğini söyleyen sağlam belgeler var mı?
  • Tek yönlü daha pitonik mi?

Örneğin:

if len(my_list) >= 4:
    x = my_list[3]
else:
    x = 'NO_ABC'

Veya:

try:
    x = my_list[3]
except IndexError:
    x = 'NO_ABC'

Bazı düşünceler ...
20.PEP şöyle diyor:

Hatalar asla sessizce geçmemelidir.
Açıkça susturulmadıkça.

Bir kullanarak Should tryyerine ait ifsessizce geçen bir hata olarak yorumlanabilir? Ve eğer öyleyse, bu şekilde kullanarak açıkça susturuyor musunuz, bu yüzden sorun değil mi?


Ben değilim değil sadece şeyler 1 yolunu yapabilir durumlara atıfta; Örneğin:

try:
    import foo
except ImportError:
    import baz

Yanıtlar:


161

Sen tercih etmeliyiz try/exceptüzerinde if/elseolduğu sonuçları ise

  • hızlanma (örneğin, fazladan aramaları engelleyerek)
  • daha temiz kod (daha az satır / daha kolay okunur)

Bunlar genellikle el ele gider.


Hız-up

Uzun bir listede bir öğeyi bulmaya çalışmak durumunda:

try:
    x = my_list[index]
except IndexError:
    x = 'NO_ABC'

indexBüyük olasılıkla listede olduğunda ve IndexError genellikle yükseltilmediğinde , hariç en iyi seçenektir . Bu şekilde, ekstra arama ihtiyacındanif index < len(my_list) .

Python istisnalar, kullanılmasını teşvik idare bir deyimdir dalış içine Python . Örneğiniz, yalnızca istisnayı (zarifçe) işlemekle kalmaz, sessizce geçmesine izin vermek yerine , istisna yalnızca bulunmayan endeksin istisnai durumunda da oluşur (dolayısıyla istisna kelimesi !).


temiz kod

Resmi Python Documentation, EAFP'den bahsediyor : İzin vermekten daha affedilmek için daha kolay ve Rob Knight , hatalardan kaçınmak yerine hataları yakalamanın daha temiz ve okunması kolay kodla sonuçlanabileceğini belirtti. Örneği şöyle diyor:

Daha kötüsü (LBYL 'atlamadan önce bak') :

#check whether int conversion will raise an error
if not isinstance(s, str) or not s.isdigit():
    return None
elif len(s) > 10:    #too many digits for int conversion
    return None
else:
    return int(s)

Daha iyi (EAFP: Bağışlama izni istemekten daha kolaydır) :

try:
    return int(s)
except (TypeError, ValueError, OverflowError): #int conversion failed
    return None

if index in mylistindeksin olası bir indeks değil, bir mylist elementi olup olmadığını test eder. Bunun if index < len(mylist)yerine istersiniz .
ychaouche

1
Bu anlamlıdır, ancak int () için hangi istisnaların atılabileceğini netleştiren belgeleri nerede bulabilirim. docs.python.org/3/library/functions.html#int Bu bilgiyi burada bulamıyorum.
BrutalSimplicity

Tam tersine, bu davayı ( kapalı.if/elsetry/catch
Doc'dan

20

Bu özel durumda, tamamen başka bir şey kullanmalısınız:

x = myDict.get("ABC", "NO_ABC")

Yine de genel olarak: Testin sık sık başarısız olmasını bekliyorsanız kullanın if. Test sadece işlemi denemeye ve başarısız olursa istisnayı yakalamaya göre pahalıysa, kullanın try. Bu koşullardan hiçbiri geçerli değilse, daha kolay okunan şeylerle devam edin.


1
Kod örneği altındaki açıklama için + 1, nokta.
ktdrv

4
Bence bu onun sorduğu şey değil ve şimdi daha da netleştirmek için gönderiyi düzenledi.
agf

9

Herhangi bir yarış koşulu olasılığı varsa, bir koruyucunun içinde tryve exceptdoğrudan yerine kullanmak her zaman yapılmalıdır. Örneğin, bir dizinin var olduğundan emin olmak istiyorsanız, bunu yapmayın:if

import os, sys
if not os.path.isdir('foo'):
  try:
    os.mkdir('foo')
  except OSError, e
    print e
    sys.exit(1)

Başka bir iş parçacığı veya işlem arasındaki dizin oluşturursa isdirve mkdir, sen çıkış yapacağız. Bunun yerine şunu yapın:

import os, sys, errno
try:
  os.mkdir('foo')
except OSError, e
  if e.errno != errno.EEXIST:
    print e
    sys.exit(1)

Bu sadece 'foo' dizini oluşturulamazsa çıkar.


7

Bir şey yapmadan önce başarısız olup olmayacağını kontrol etmek önemsizse, muhtemelen bunu tercih etmelisiniz. Sonuçta, istisnalar oluşturmak (ilişkili geri bildirimleri dahil) zaman alır.

İstisnalar aşağıdakiler için kullanılmalıdır:

  1. beklenmedik şeyler, ya da ...
  2. birden fazla mantık seviyesine atlamanız gereken şeyler (örn break.
  3. istisnayı önceden ne ele geçireceğini tam olarak bilmediğiniz şeyler, ya da ...
  4. başarısızlık için önceden kontrol etmenin pahalı olduğu şeyler (sadece operasyonu denemeye göre)

Not çoğu zaman o, gerçek cevap "ne" dir - örneğin, ilk örnekte, gerçekten ne gerekir sadece kullanmak yapmak .get()varsayılan sağlamaktır:

x = myDict.get('ABC', 'NO_ABC')

bunun dışında maalesef if 'ABC' in myDict: x = myDict['ABC']; else: x = 'NO_ABC'genellikle kullanmaktan daha hızlı get. Bunun en önemli kriter olduğunu söylememek, ama farkında olunması gereken bir şey.
agf

@agf: Net ve özlü kod yazmak daha iyidir. Bir şeyin daha sonra optimize edilmesi gerekiyorsa, geri gelip yeniden yazmak kolaydır, ancak c2.com/cgi/wiki?PrematureOptimization
Amber

Bunu biliyorum ; benim açımdan oldu if / elseve try / exceptonların yerine sahip olabilir olaya özgü alternatifler varken bile farklı performans özelliklerine sahip çünkü.
agf

@agf, get () yönteminin gelecekteki sürümlerde (en azından) açıkça arama kadar hızlı olacak şekilde geliştirilip geliştirilmeyeceğini biliyor musunuz? Bu arada, iki kez bakarken (d: d ['ABC'] içindeki 'ABC' gibi), KeyError hariç ... d ['ABC'] deniyor: ... en hızlı değil mi?
Remi

2
@Remi Yavaş kısmı .get(), Python düzeyinde özellik arama ve işlev çağrısı ek yüküdür ; yerleşik anahtar kelimeleri kullanarak temelde C doğru gidiyor. Ben yakında çok daha hızlı olacak sanmıyorum. Kadarıyla gibi ifvs try, okuma dict.get () yöntemi döner bir işaretçi bazı performans bilgisi vardır. İsabetlerin cevapsızlığa oranı ( tryanahtar neredeyse her zaman mevcutsa daha hızlı olabilir) ve sözlüğün boyutu.
agf

5

Diğer gönderilerin de belirttiği gibi, duruma bağlıdır. Verilerinizin geçerliliğini önceden kontrol etmek yerine, özellikle büyük projelerde kullanırken try / haricinde birkaç tehlike vardır.

  • Try bloğundaki kod, istisna yakalanmadan önce her türlü tahribatı bozma şansına sahip olabilir - önceden bir if ifadesiyle proaktif olarak kontrol ederseniz, bundan kaçınabilirsiniz.
  • Deneme bloğunuzda çağrılan kod, TypeError veya ValueError gibi ortak bir istisna türü oluşturursa, yakalamayı beklediğiniz aynı istisnayı yakalayamayabilirsiniz - bu, almadan önce veya sonra aynı istisna sınıfını yükselten başka bir şey olabilir istisnanızın ortaya çıkabileceği çizgi.

örneğin, varsayalım:

try:
    x = my_list[index_list[3]]
except IndexError:
    x = 'NO_ABC'

IndexError, index_list veya my_list öğelerini almaya çalışırken gerçekleşip gerçekleşmediği hakkında hiçbir şey söylemez.


4

İf yerine bir try kullanmak sessizce geçen bir hata olarak mı yorumlanmalıdır? Ve eğer öyleyse, bu şekilde kullanarak açıkça susturuyor musunuz, bu yüzden sorun değil mi?

Kullanmak try, bir hatanın sessizce geçmesinin tersi olan bir hatanın geçebileceğini kabul eder. Kullanmak excepthiç geçmemesine neden oluyor.

Mantık daha karmaşık try: except:olan durumlarda kullanmak tercih edilir if: else:. Basit karmaşık olmaktan iyidir; karmaşık karmaşık olmaktan iyidir; affetmek için izin istemekten daha kolaydır.

"Hataların asla sessizce geçmemesi" konusunda uyarmak, kodun bildiğiniz bir istisnayı artırabileceği ve tasarımınızın olasılığı kabul ettiği, ancak istisna ile başa çıkacak şekilde tasarlamadığınız durumdur. Bir hatayı açıkça susturmak, bence, passbir exceptbloktaki gibi bir şey yapmak olurdu , bu sadece "hiçbir şey yapmamak" ın gerçekten belirli bir durumda doğru hata işlemesi olduğu anlayışıyla yapılmalıdır. (Bu, iyi yazılmış bir kodda bir yorum yapmak istediğim birkaç kez biridir, muhtemelen gerçekten gereklidir.)

Ancak, özel örneğinizde, ikisi de uygun değildir:

x = myDict.get('ABC', 'NO_ABC')

Herkesin bunu işaret etmesinin nedeni - genel olarak anlama arzunuzu ve daha iyi bir örnek oluşturamamanızı kabul etseniz bile - birçok durumda eşdeğer yan adımların aslında var olması ve bunları aramak, sorunu çözmenin ilk adımı.


3

try/exceptKontrol akışı için her kullandığınızda kendinize sorun:

  1. Ne zaman görmek kolay mı tryBloğun ne zaman başarılı olduğunu ve ne zaman başarısız ?
  2. İçindeki tüm yan etkilerin farkında mısınız?tryBlok ?
  3. Tüm vakaların farkında mısınız?tryBloğun istisnayı attığı ?
  4. tryBloğun uygulanması değişirse, kontrol akışınız hala beklendiği gibi davranır mı?

Bu sorulardan bir veya daha fazlasının cevabı 'hayır' ise, sormak için çok fazla affetme olabilir; büyük olasılıkla gelecekteki benliğinizden.


Bir örnek. Son zamanlarda şöyle görünüyordu daha büyük bir projede kod gördüm:

try:
    y = foo(x)
except ProgrammingError:
    y = bar(x)

Programcı ile konuşurken amaçlanan kontrol akışının:

X bir tamsayı ise y = foo (x) yapın.

X bir tamsayı listesiyse, y = bar (x) yapın.

Bu foo, bir veritabanı sorgusu yapıldığından ve xbir tam sayı ProgrammingErrorolsaydı xve bir liste olsaydı atarsa sorgu başarılı olacağı için işe yaradı .

Burada kullanmak try/exceptkötü bir seçimdir:

  1. İstisnanın adı ProgrammingError, asıl problemi (x bir tamsayı değildir), bu da neler olup bittiğini görmeyi zorlaştırır.
  2. Zaman ProgrammingErrorharcayan bir veritabanı çağrısı sırasında yükseltilir. fooBir istisna atmadan önce veritabanına bir şeyler yazan veya başka bir sistemin durumunu değiştiren şeyler ortaya çıkarsa işler gerçekten korkunç olurdu .
  3. ProgrammingErrorYalnızca xtamsayıların bir listesi olduğunda ortaya çıkıp çıkmadığı belirsizdir . Örneğin, fooveritabanı sorgusunda bir yazım hatası olduğunu varsayalım . Bu da a'yı yükseltebilir ProgrammingError. Sonuç, bar(x)şimdi xbir tamsayı olduğunda da denir . Bu, şifreli istisnaları artırabilir veya öngörülemeyen sonuçlar doğurabilir.
  4. try/exceptBlok gelecekteki tüm uygulamaları için bir gereksinim ekler foo. Ne zaman değişsek foo, şimdi listeleri nasıl ele aldığını düşünmeli ve listenin hiç hata ProgrammingErrorvermediğinden AttributeErrorya da hiç hata vermediğinden emin olmalıyız .

0

Genel bir anlam için, Python'da Deyimler ve Anti-Deyimler okumayı düşünebilirsiniz : İstisnalar .

Özel durumunuzda, diğerlerinin belirttiği gibi, şunları kullanmalısınız dict.get():

get (anahtar [, varsayılan])

Anahtar sözlükte ise anahtarın değerini döndürür, aksi takdirde varsayılan değerdir. Varsayılan belirtilmezse, varsayılan olarak Yok olarak ayarlanır, böylece bu yöntem hiçbir zaman KeyError oluşturmaz.


Bağlantının bu durumu hiç kapsadığını düşünmüyorum. Örnekleri, gerçek durumları temsil eden şeyleri işlemekle ilgilidir , beklenen durumlar için istisna işlemeyi kullanıp kullanmayacağınızla ilgili değildir.
agf

İlk bağlantı eski.
Timofei Bondarev
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.