Bir liste anlayışındaki istisnalar nasıl ele alınır?


121

Python'da her yinelemenin bir istisna oluşturabileceği bir liste anlayışım var.

Örneğin , eğer varsa:

eggs = (1,3,0,3,2)

[1/egg for egg in eggs]

ZeroDivisionError3. elementte bir istisna elde edeceğim .

Bu istisnayı nasıl ele alabilirim ve liste kavrayışını yürütmeye nasıl devam edebilirim?

Aklıma gelen tek yol, yardımcı bir işlev kullanmaktır:

def spam(egg):
    try:
        return 1/egg
    except ZeroDivisionError:
        # handle division by zero error
        # leave empty for now
        pass

Ama bu bana biraz hantal görünüyor.

Bunu Python'da yapmanın daha iyi bir yolu var mı?

Not: Bu, benim yaptığım basit bir örnektir ( yukarıdaki " örneğin " kısmına bakın) çünkü gerçek örneğim biraz bağlam gerektiriyor. Sıfır hataya bölmekten kaçınmakla ilgilenmiyorum, ancak bir liste anlayışında istisnaları ele almakla ilgileniyorum.


4
İstisnaları ele almak için bir ifade eklemek için bir PEP 463 var . Sizin örneğinizde bu olurdu [1/egg except ZeroDivisionError: None for egg in (1,3,0,3,2)]. Ama hala taslak modunda. İçimden gelen his, kabul edilmeyeceği yönünde. Imho ifadeler çok dağınık (daha karmaşık kombinasyonları (çoklu mantıksal operatörler, karmaşık comprehensions, vs) sahip çoklu istisnaları kontrol alabilirsiniz
STF'nizi

1
Bu özel örnek için, ndarrayiçinde uygun ayarlara sahip bir numpy kullanabileceğinizi unutmayın np.seterr. Bu sonuçlanır 1/0 = nan. Ancak bunun, bu ihtiyacın ortaya çıktığı diğer durumlara genellemediğinin farkındayım.
gerrit

Yanıtlar:


97

Python'da bir istisnayı görmezden gelmenize (veya istisnalar durumunda alternatif değerler & c döndürmenize) izin veren yerleşik bir ifade yoktur, bu nedenle, kelimenin tam anlamıyla "bir liste anlayışındaki istisnaları ele almak" imkansızdır çünkü bir liste anlama bir ifadedir başka bir ifade içeren, daha fazlası değil (yani, içermez hiçbir ifade yoktur ve yalnızca ifadeler istisnaları yakalayabilir / yok sayabilir / işleyebilir).

İşlev çağrıları ifadedir ve işlev gövdeleri istediğiniz tüm ifadeleri içerebilir, bu nedenle istisnaya eğilimli alt ifadenin değerlendirmesini bir işleve devretmek, fark ettiğiniz gibi, uygulanabilir bir çözümdür (diğerleri, uygun olduğunda, diğer cevaplarda da önerildiği gibi, istisnalara neden olabilecek değerleri kontrol eder).

"Bir liste kavrayışındaki istisnaların nasıl ele alınacağı" sorusuna verilen doğru yanıtların hepsi bu gerçeğin bir parçasını ifade etmektedir: 1) kelimenin tam anlamıyla, yani sözlü olarak anlamanın İÇİNDE, yapamazsınız; 2) pratik olarak, işi bir işleve devredersiniz veya mümkün olduğunda hataya açık değerleri kontrol edersiniz. Bunun bir cevap olmadığına dair tekrarlanan iddianız bu nedenle temelsizdir.


14
Anlıyorum. Yani tam bir cevap şudur: 1. Bir işlev kullanmalıyım, 2. liste anlamayı kullanmamalıyım 3. istisnayı halletmek yerine önlemeye çalışmalıyım.
Nathan Fellman

9
"Liste anlamalarını kullanmama" yı, "liste anlamalarındaki istisnaların nasıl ele alınacağı" yanıtının bir parçası olarak görmüyorum, ancak bunu mantıklı bir şekilde " sözlü olarak LC'de " olası bir sonucu olarak görebilirsiniz , bunu yapmak mümkün değil "istisnaları ele alın", bu aslında gerçek cevabın ilk kısmıdır.
Alex Martelli

Bir üreteç ifadesinde veya oluşturucu anlayışında bir hata yakalayabilir misiniz?

1
@AlexMartelli, bir istisna cümlesi gelecekteki python sürümlerinde çalışmak o kadar zor olur mu? [x[1] for x in list except IndexError pass]. Tercüman denemek için geçici bir işlev oluşturamadı x[1]mı?
alancalvitti

@Nathan, 1,2,3, fonksiyonel veri akışlarında çok büyük baş ağrılarına dönüşür, burada 1. tipik olarak fonksiyonları lambdalar aracılığıyla satır içi yapmak ister; 2. alternatif, işlevsel paradigmayı ihlal eden ve hataya açık koda yol açan çok sayıda iç içe geçmiş döngü kullanmaktır; 3. Genellikle hatalar geçici ve gizli karmaşık veri kümeleridir ve veri araçları için latin kelime olarak verilir ve bu nedenle kolayca önlenemez.
alancalvitti

119

Bu sorunun oldukça eski olduğunun farkındayım, ancak bu tür şeyleri kolaylaştırmak için genel bir işlev de oluşturabilirsiniz:

def catch(func, handle=lambda e : e, *args, **kwargs):
    try:
        return func(*args, **kwargs)
    except Exception as e:
        return handle(e)

Sonra anladığın kadarıyla:

eggs = (1,3,0,3,2)
[catch(lambda : 1/egg) for egg in eggs]
[1, 0, ('integer division or modulo by zero'), 0, 0]

Elbette, varsayılan tutamaç işlevini istediğiniz gibi yapabilirsiniz (varsayılan olarak 'Yok'u döndürmeyi tercih edeceğinizi söyleyin).

Bunun size veya bu sorunun gelecekteki izleyicilerine yardımcı olacağını umuyoruz!

Not: python 3'te, yalnızca 'tutamaç' argüman anahtar kelimesini yapardım ve onu argüman listesinin sonuna koyardım. Bu, gerçekte tartışmaları ve benzerlerini yakalamak yoluyla çok daha doğal hale getirir.


2
son derece yararlı, teşekkürler. Teorik yorumlara katılıyorum, ancak bu, defalarca karşılaştığım bir problemi çözmek için pratik bir yaklaşım gösteriyor.
Paul

2
Harika cevap. Ben öneririm Bir mod geçiyor argsve kwargshem de sap üzerine. Bu şekilde , eggkodlanmış bir kod yerine 0veya yaptığınız gibi bir istisna yerine söyleyebilirsiniz .
Mad Fizikçi

3
İstisna tipini isteğe bağlı bir argüman olarak da isteyebilirsiniz (istisna türleri parametrik hale getirilebilir mi?), Böylece beklenmedik istisnalar tüm istisnaları yok saymak yerine yukarı doğru fırlatılır.
00prometheus

3
@Bryan, "python 3'te, sadece 'handle' argüman anahtar kelimesini yapar ve onu argüman listesinin sonuna koyarım." handlesonra yerleştirmeyi denedi **kwargve bir SyntaxError. Gibi kwargs.get('handle',e)mi demek istiyorsun ?
alancalvitti

21

Kullanabilirsiniz

[1/egg for egg in eggs if egg != 0]

bu, sıfır olan öğeleri atlayacaktır.


28
Bu, bir liste anlayışındaki istisnaların nasıl ele alınacağı sorusuna cevap vermez.
Nathan Fellman

8
evet, öyle. istisnaları ele alma ihtiyacını ortadan kaldırır. evet, bu her zaman doğru çözüm değil, ama yaygın bir çözüm.
Peter

3
Anlıyorum. Yorumu geri alıyorum (ancak bu kısa 'tartışma' yanıtı geliştirdiği için onu silmeyeceğim).
Nathan Fellman

11

Hayır daha iyi bir yol yok. Pek çok durumda, Peter'ın yaptığı gibi sakınmayı kullanabilirsiniz.

Diğer seçeneğiniz, anlamaları kullanmamaktır

eggs = (1,3,0,3,2)

result=[]
for egg in eggs:
    try:
        result.append(egg/0)
    except ZeroDivisionError:
        # handle division by zero error
        # leave empty for now
        pass

Bunun daha hantal olup olmadığına karar vermek size kalmış


1
Burada anlamaları nasıl kullanırım?
Nathan Fellman

@Nathan: yapmazdın. gnibbler diyor ki: Hayır daha iyi bir yol yok
SilentGhost

Üzgünüm ...
Cevabındaki

4

Bence ilk soruyu soran ve Bryan Head tarafından da önerildiği gibi, yardımcı bir işlev iyi ve hantal değil. Tüm işi yapan tek bir sihirli kod satırı her zaman mümkün değildir, bu nedenle fordöngülerden kaçınmak isteyen bir yardımcı işlev mükemmel bir çözümdür . Ancak bunu şu şekilde değiştirirdim:

# A modified version of the helper function by the Question starter 
def spam(egg):
    try:
        return 1/egg, None
    except ZeroDivisionError as err:
        # handle division by zero error        
        return None, err

Çıktı bu olacak [(1/1, None), (1/3, None), (None, ZeroDivisionError), (1/3, None), (1/2, None)]. Bu cevapla istediğiniz şekilde devam etmek için tam kontrole sahipsiniz.


Güzel. Bu Either, bazı işlevsel programlama dillerindeki (Scala gibi) türe çok benziyor , burada bir Eithertürden bir değer bulunabilir, ancak her ikisi birden olamaz. Tek fark, bu dillerde hatayı sol tarafa ve değeri sağ tarafa koymanın deyimsel olmasıdır. İşte daha fazla bilgi içeren bir makale .
Alex Palmer

3

Bundan bahseden herhangi bir cevap görmedim. Ancak bu örnek, bilinen başarısız durumlar için bir istisnanın ortaya çıkmasını önlemenin bir yolu olabilir.

eggs = (1,3,0,3,2)
[1/egg if egg > 0 else None for egg in eggs]


Output: [1, 0, None, 0, 0]

Bu, bu cevapla aynı değil mi? stackoverflow.com/a/1528244/1084
Nathan Fellman

İnce bir fark var. Filtreleme, girdi listesi yerine çıktıya uygulanır. Gönderilen örnekte görebileceğiniz gibi, bir istisnaya neden olabilecek durum için "Hiçbiri" seçeneğini belirttim.
Slakker
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.