2016-03-22 3 views
16

В python есть разница между raise и raise e в блоке за исключением?Разница между «рейзом» и «поднять e»?

dis показывает мне разные результаты, но я не знаю, что это значит.

Каково конечное поведение обоих?

import dis 
def a(): 
    try: 
     raise Exception() 
    except Exception as e: 
     raise 


def b(): 
    try: 
     raise Exception() 
    except Exception as e: 
     raise e 

dis.dis(a) 
# OUT: 4   0 SETUP_EXCEPT   13 (to 16) 
# OUT: 5   3 LOAD_GLOBAL    0 (Exception) 
# OUT:    6 CALL_FUNCTION   0 
# OUT:    9 RAISE_VARARGS   1 
# OUT:    12 POP_BLOCK   
# OUT:    13 JUMP_FORWARD   22 (to 38) 
# OUT: 6  >> 16 DUP_TOP    
# OUT:    17 LOAD_GLOBAL    0 (Exception) 
# OUT:    20 COMPARE_OP    10 (exception match) 
# OUT:    23 POP_JUMP_IF_FALSE  37 
# OUT:    26 POP_TOP    
# OUT:    27 STORE_FAST    0 (e) 
# OUT:    30 POP_TOP    
# OUT: 7   31 RAISE_VARARGS   0 
# OUT:    34 JUMP_FORWARD    1 (to 38) 
# OUT:   >> 37 END_FINALLY   
# OUT:   >> 38 LOAD_CONST    0 (None) 
# OUT:    41 RETURN_VALUE   
dis.dis(b) 
# OUT: 4   0 SETUP_EXCEPT   13 (to 16) 
# OUT: 5   3 LOAD_GLOBAL    0 (Exception) 
# OUT:    6 CALL_FUNCTION   0 
# OUT:    9 RAISE_VARARGS   1 
# OUT:    12 POP_BLOCK   
# OUT:    13 JUMP_FORWARD   25 (to 41) 
# OUT: 6  >> 16 DUP_TOP    
# OUT:    17 LOAD_GLOBAL    0 (Exception) 
# OUT:    20 COMPARE_OP    10 (exception match) 
# OUT:    23 POP_JUMP_IF_FALSE  40 
# OUT:    26 POP_TOP    
# OUT:    27 STORE_FAST    0 (e) 
# OUT:    30 POP_TOP    
# OUT: 7   31 LOAD_FAST    0 (e) 
# OUT:    34 RAISE_VARARGS   1 
# OUT:    37 JUMP_FORWARD    1 (to 41) 
# OUT:   >> 40 END_FINALLY   
# OUT:   >> 41 LOAD_CONST    0 (None) 
# OUT:    44 RETURN_VALUE   
+5

Возможный дубликат [рейз без аргументов] (http://stackoverflow.com/questions/18001721/raise-with-no-argument) –

+1

@ Jérôme Я не думаю, что это дубликат этого. Этот вопрос касается разницы между версиями no-arg и with-arg в этом конкретном примере кода. Этот вопрос связан с тем, как обычно работает no-arg. Они связаны, но не дублируются. – Daenyth

+0

Да, и принятый ответ интересен именно по этой причине. Если есть способ развязать вопрос, я сделаю это. –

ответ

14

В этом случае нет никакой разницы. raise без аргументов will always raise the last exception thrown (который также доступен с sys.exc_info()).

Причина, по которой байт-код отличается, потому что Python является динамическим языком, и интерпретатор действительно не знает, что e ссылается на (немодифицированное) исключение, которое в настоящее время обрабатывается. Но это не всегда может быть случай, рассмотрим:

try: 
    raise Exception() 
except Exception as e: 
    if foo(): 
     e = OtherException() 
    raise e 

Что такое e сейчас? Невозможно сказать при компиляции байт-кода (только когда на самом деле работает программы).

В простых примерах, подобных вашим, возможно, что интерпретатор Python может «оптимизировать» байт-код, но пока никто этого не сделал. И почему они должны? В лучшем случае это микро-оптимизация и может все еще ломаться тонкими способами в неясных условиях. Существует много других фруктов, которые висят намного ниже этого и более питательны для загрузки ;-)

+0

Я не пытаюсь оптимизировать, я просто задавался вопросом, было ли поведение в этом конкретном случае чем-то другим. – Daenyth

+1

@ Даенит. Да, я понимаю, что примечание по оптимизации было просто объяснить, почему Python не «оптимизирует» байт-код в простые примеры ;-) – Carpetsmoker

+0

Ваше редактирование делает это более понятным, спасибо – Daenyth

2

Можно удалить «последнее исключение» (т. Е. Результат sys.exc_info()) info с sys.exc_clear(). Например, это произойдет, если блок catch вызывает функцию foo(), которая сама имеет специальную обработку ошибок.

В этом случае raise с аргументом и без него будет означать разные вещи. raise e все равно будет ссылаться на исключение, пойманное на несколько строк выше, а стенограмма raise попытается поднять None, что является ошибкой.

3

Существует разница в обратных трассах, которые генерируют две формы.

Используя raise, этот код:

try: 
    int("hello") 
except ValueError as e: 
    raise 

дает следующую трассировку:

Traceback (most recent call last): 
    File "myfile.py", line 2, in <module> 
    int("hello") 
ValueError: invalid literal for int() with base 10: 'hello' 

Использование raise e следующим образом:

try: 
    int("hello") 
except ValueError as e: 
    raise e 

дает следующую трассировку

Traceback (most recent call last): 
    File "myfile.py", line 4, in <module> 
    raise e 
ValueError: invalid literal for int() with base 10: 'hello' 

Разница заключается в том, что в raise случае, правильная линия ссылки на первоисточник, за исключением цитируется в трассировку, но в raise e случае отслеживающий ссылается на raise e линию не первопричиной.

Поэтому я рекомендую всегда использовать raise, а не raise e.

+0

Поведение, которое вы описываете, относится к Python 2, но не Python 3. В Python 3 'raise e' не проглатывает исходный источник и на самом деле показывает строго * больше * информация в этом случае. –

Смежные вопросы