Як поводитися з винятками в списку розумінь?


120

У мене є певне розуміння списку в Python, в якому кожна ітерація може спричинити виняток.

Наприклад , якщо у мене є:

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

[1/egg for egg in eggs]

Я отримаю ZeroDivisionErrorвиняток у 3-му елементі.

Як я можу впоратися з цим винятком і продовжувати виконувати розуміння списку?

Єдиний спосіб, що я можу придумати, - це використовувати функцію помічника:

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

Але це виглядає для мене трохи громіздко.

Чи є кращий спосіб зробити це в Python?

Примітка. Це простий приклад (див. " Для прикладу " вище), який я надумав, оскільки для мого реального прикладу потрібен певний контекст. Мене не цікавить уникнення поділу на нульові помилки, але обробка виключень у розумінні списку.


4
Є PEP 463, щоб додати вираз для обробки винятків. У вашому прикладі це було б [1/egg except ZeroDivisionError: None for egg in (1,3,0,3,2)]. Але він все ще знаходиться в режимі чернетки. Моє відчуття кишки полягає в тому, що це не буде прийнято. Вирази Imho можуть стати занадто безладним (перевірка декількох винятків, складніші комбінації (кілька логічних операторів, складне розуміння тощо)
cfi

1
Зауважте, що для цього конкретного прикладу ви можете використовувати нумеру ndarrayз відповідними налаштуваннями в np.seterr. Це призведе до 1/0 = nan. Але я усвідомлюю, що це не узагальнює інших ситуацій, коли виникає така потреба.
Герріт

Відповіді:


96

У Python немає вбудованого виразу, який дозволяє ігнорувати виняток (або повертати альтернативні значення & c у разі винятків), тому неможливо, буквально кажучи, "обробляти винятки в розумінні списку", оскільки розуміння списку - це вираз що містить інший вираз, більше нічого (тобто відсутні твердження, і лише висловлювання можуть вловлювати / ігнорувати / обробляти винятки).

Виклики функцій - це вираз, і органи функцій можуть включати всі необхідні оператори, тому делегування оцінки схильного до винятку підвиразу функції, як ви вже помітили, є одним з можливих рішень (інші, коли це можливо, є перевірки значень, які можуть спровокувати винятки, як це також пропонується в інших відповідях).

Правильні відповіді на запитання "як поводитися з винятками в розумінні списку" - це все виражає частину всієї цієї істини: 1) буквально, тобто лексично В самому розумінні ви не можете; 2) практично ви делегуєте завдання функції або перевіряєте значення схильності до помилок, коли це можливо. Таким чином, ваше неодноразове твердження, що це не відповідь, є необґрунтованим.


14
Я бачу. Отже, повною відповіддю було б те, що я повинен: 1. використовувати функцію 2. не використовувати розуміння списку 3. намагатися запобігти виключенню, а не обробляти його.
Натан Фелман

9
Я не бачу "не використовувати розуміння списків" як частину відповіді на "як поводитися з винятками в розумінні списків", але, мабуть, ви могли б розумно сприймати це як можливий наслідок " лексично В ЖК, це неможливо обробляти винятки », що справді є першою частиною буквальної відповіді.
Алекс Мартеллі

Чи можете ви виявити помилку в виразі генератора чи розумінні генератора?

1
@AlexMartelli, хіба крім пункту буде важко працювати над майбутніми версіями python? [x[1] for x in list except IndexError pass]. Чи не міг перекладач створити тимчасову функцію для спробу x[1]?
alancalvitti

@Nathan, 1,2,3 вище перетворюються на величезні головні болі у функціональних потоках даних, де 1. типово хочеться вбудовувати функції за допомогою лямбда; 2. альтернативою є використання безлічі вкладених циклів, які порушують функціональну парадигму і призводять до схильності до помилок; 3. Часто помилки - це спеціальні та приховані складні набори даних, які, як означає латинське слово для даних, не можна легко запобігти.
alancalvitti

118

Я усвідомлюю, що це питання досить старе, але ви також можете створити загальну функцію, щоб полегшити подібні речі:

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

Потім, у вашому розумінні:

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

Звичайно, ви можете зробити функцію ручки за замовчуванням все, що завгодно (скажімо, ви бажаєте повернути "Нічого" за замовчуванням).

Сподіваємось, це допоможе вам або будь-яким майбутнім глядачам цього питання!

Примітка: у python 3 я б зробив лише ключове слово аргументу "handle" і ставлю його в кінці списку аргументів. Це зробило б насправді аргументи та подібне через вилов набагато природніше.


2
надзвичайно корисно, дякую. Хоча я згоден з теоретичними коментарями, це свідчить про практичний підхід до вирішення проблеми, яку я неодноразово зустрічав.
Пол

2
Зробіть відповідь. Один мод, який я б запропонував, також проходить argsі kwargsдля обробки. Таким чином, ви можете повернутися скажіть eggзамість жорсткого коду 0чи винятку, як це робите.
Божевільний фізик

3
Ви також можете захотіти тип винятку як необов'язковий аргумент (чи можуть бути параметризовані типи винятків?), Щоб несподівані винятки були підкинуті вгору, а не ігнорування всіх винятків.
00прометей

3
@Bryan, чи можете ви вказати код для "в python 3, я б зробив лише ключове слово" аргумент "аргументу і ставлю його в кінці списку аргументів." спробував розмістити handleпісля **kwargі отримав SyntaxError. Ви маєте на увазі дереференцію як kwargs.get('handle',e)?
alancalvitti

21

Можна використовувати

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

це просто пропустить нульові елементи.


28
Це не відповідає на питання, як поводитися з винятками в розумінні списку.
Натан Фелман

8
гмм, так, це так. це унеможливлює обробку винятків. так, це не завжди правильне рішення, але це звичайне.
Пітер

3
Я розумію. Я беру назад коментар (хоча я його не видаляю, оскільки ця коротка "дискусія" покращується у відповіді).
Натан Фелман

11

Ні, немає кращого способу. У багатьох випадках ви можете використовувати уникнення, як це робить Петро

Ваш інший варіант - не використовувати розуміння

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

Ви самі вирішуєте, чи це більш громіздко, чи ні


1
Як би я тут використовував розуміння?
Натан Фелман

@Nathan: ти б не зробив. gnibbler каже: Ні, немає кращого способу
SilentGhost

Вибачте ... Я пропустив "не" у його відповіді :-)
Натан Феллман

4

Я думаю, що функція помічника, як це запропонував той, хто задає початкове запитання, а також Брайан Хед, - це добре і зовсім не громіздко. Єдиний рядок магічного коду, який виконує всю роботу, просто не завжди можливий, тому функція помічника є ідеальним рішенням, якщо потрібно уникати forциклів. Однак я змінив би це:

# 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

Вихід буде таким [(1/1, None), (1/3, None), (None, ZeroDivisionError), (1/3, None), (1/2, None)]. З цією відповіддю ви перебуваєте в повному контролі, щоб продовжувати будь-який спосіб, який вам захочеться.


Приємно. Це дуже схоже на Eitherтип у деяких функціональних мовах програмування (наприклад, Scala), де а Eitherможе містити значення того чи іншого типу, але не обидва. Різниця полягає лише в тому, що в цих мовах ідіоматично ставити помилку з лівого боку та значення на праву. Ось стаття з додатковою інформацією .
Алекс Палмер

3

Я не бачив жодної відповіді про це. Але цей приклад був би одним із способів запобігти появі винятку для відомих невдалих справ.

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


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

Хіба це не те саме, що ця відповідь? stackoverflow.com/a/1528244/1084
Натан Fellman

Є тонка різниця. Фільтрація застосовується на вихідному, а не на вхідному списку. Як ви бачите у розміщеному прикладі, я позначав "Ні" для випадку, який може спричинити виняток.
Slakker
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.