2016-03-22 8 views
16

W python, czy istnieje różnica między raise i raise e w bloku z wyjątkiem?Różnica między "podniesieniem" a "podniesieniem e"?

dis pokazuje mi różne wyniki, ale nie wiem, co to znaczy.

Jakie są zachowania końcowe obu?

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

Możliwy duplikat [przebicie bez argumentu] (http://stackoverflow.com/questions/18001721/raise-with-no-argument) –

+1

@ Jérôme Nie sądzę, że jest to duplikat tego. To pytanie dotyczy różnicy między wersjami no-arg i with-arg w tej konkretnej próbce kodu. Pytanie dotyczy tego, jak ogólnie działa no-arg. Są spokrewnione, ale nie duplikaty. – Daenyth

+0

Tak, a zaakceptowana odpowiedź jest interesująca właśnie z tego powodu. Jeśli istnieje sposób, aby odeprzeć pytanie, zrobię to. –

Odpowiedz

14

Nie ma różnicy w tym przypadku. raise bez argumentów will always raise the last exception thrown (który jest również dostępny z sys.exc_info()).

Powodem, dla którego kod bajtowy jest inny, jest to, że język Python jest językiem dynamicznym, a tłumacz nie "wie", że e odnosi się do (niezmodyfikowanego) wyjątku, który jest aktualnie obsługiwany. Ale to nie zawsze może być przypadek, należy rozważyć:

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

Co jest e teraz? Nie ma sposobu, aby powiedzieć, kiedy kompiluje się kod bajtowy (tylko wtedy, gdy faktycznie program działa).

W prostych przykładach, takich jak Twój, interpreter języka Python może "zoptymalizować" kod bajtowy, ale jak dotąd nikt tego nie zrobił. I dlaczego mieliby to robić? W najlepszym przypadku jest to mikrooptymalizacja i może wciąż łamać się w subtelny sposób w mrocznych warunkach. Jest dużo innych owoców, które zwisają dużo niżej i są bardziej pożywne do załadowania ;-)

+0

Nie próbuję optymalizować, tylko zastanawiałem się, czy zachowanie było inne w tym konkretnym przypadku. – Daenyth

+1

@Daenyth Tak, rozumiem, że nota optymalizacyjna miała jedynie wyjaśnić, dlaczego Python nie "optymalizuje" kodu bajtowego w proste przykłady ;-) – Carpetsmoker

+0

Twoja zmiana sprawia, że ​​jest to bardziej zrozumiałe, dzięki – Daenyth

2

Możliwe jest wyczyszczenie "ostatniego wyjątku" (tj. Wyniku sys.exc_info()) z pomocą sys.exc_clear(). Na przykład może się tak zdarzyć, jeśli blok catch zwany jest funkcją foo(), która sama zajmuje się obsługą błędów.

W takim przypadku raise z argumentem i bez niego oznaczałoby różne rzeczy. raise e nadal miałoby odniesienie do wyjątku złapanego kilka linii powyżej, podczas gdy stenogram raise próbowałby podnieść None, co jest błędem.

3

Istnieje różnica w śladach, które generują dwa formularze.

Stosując raise, kod:

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

daje następujące ślad:

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

za pomocą raise e następująco:

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

daje następujące ślad

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

Różnica polega na tym, że w przypadku raise poprawna linia odwoływania się do oryginalnego źródła wyjątkiem cytowany w śledzenia wstecznego, lecz w przypadku raise e ślad błędu odwołuje linię raise e nie oryginalne przyczyny.

Dlatego polecam zawsze używać raise zamiast raise e.

+0

Opisywane zachowanie jest prawdziwe w Pythonie 2, ale nie w Pythonie 3. W Pythonie 3, 'raise e' nie połyka oryginalnego źródła i faktycznie pokazuje dokładnie * więcej * informacji w tym przypadku. –