2012-06-05 6 views
13

Mam wiele wierszy z rzędu, które mogą rzucić wyjątek, ale bez względu na to, powinien nadal kontynuować następny wiersz. Jak to zrobić, jeśli pojedynczo nie próbujesz przechwycić każdej instrukcji, która może rzucić wyjątek?Jak zapobiec próbom złapania każdej możliwej linii w python?

try: 
    this_may_cause_an_exception() 
    but_I_still_wanna_run_this() 
    and_this() 
    and_also_this() 
except Exception, e: 
    logging.exception('An error maybe occured in one of first occuring functions causing the others not to be executed. Locals: {locals}'.format(locals=locals())) 

Zobaczmy nad kodem, wszystkie funkcje mogą rzucać wyjątki, ale w dalszym ciągu powinno wykonać następnego FUNKCJE bez względu na to zwrócił wyjątek czy nie. Czy jest to dobry sposób na zrobienie tego?

ja nie chcę to zrobić:

try: 
    this_may_cause_an_exception() 
except: 
    pass 
try: 
    but_I_still_wanna_run_this() 
except: 
    pass 
try: 
    and_this() 
except: 
    pass 
try: 
    and_also_this() 
except: 
    pass 

myślę kod powinien nadal kontynuować jazdę po drodze wyjątku tylko wtedy, gdy wyjątek jest krytyczna (Komputer będzie spalić lub cały system dostanie pomieszane, powinno zatrzymaj cały program, ale dla wielu małych rzeczy są również zgłaszane wyjątki, takie jak połączenie nie powiodło się itd.). Zwykle nie mam żadnych problemów z obsługą wyjątków, ale w tym przypadku używam biblioteki innej firmy, która łatwo zgłasza wyjątki dla małych rzeczy.

Po przeanalizowaniu odpowiedzi m4spy pomyślałem, że nie byłoby możliwe posiadanie dekoratora, który pozwoli na uruchomienie każdej linii w funkcji, nawet jeśli jeden z nich zgłasza wyjątek.

Coś takiego byłoby cool:

def silent_log_exceptions(func): 
    @wraps(func) 
    def _wrapper(*args, **kwargs): 
     try: 
      func(*args, **kwargs) 
     except Exception: 
      logging.exception('...') 
      some_special_python_keyword # which causes it to continue executing the next line 
    return _wrapper 

Albo coś takiego:

def silent_log_exceptions(func): 
    @wraps(func) 
    def _wrapper(*args, **kwargs): 
     for line in func(*args, **kwargs): 
      try: 
       exec line 
      except Exception: 
       logging.exception('...') 
    return _wrapper 



@silent_log_exceptions 
def save_tweets(): 
    a = requests.get('http://twitter.com) 
    x = parse(a) 
    bla = x * x 
+3

Żadna z tych stylów kodowania czuć strasznie pythonic ... Nie jestem pewien, co się sugerować, ale ja czekam na odpowiedź. – Crisfole

Odpowiedz

15
for func in [this_may_cause_an_exception, 
      but_I_still_wanna_run_this, 
      and_this, 
      and_also_this]: 
    try: 
     func() 
    except: 
     pass 

Są dwie rzeczy zauważyć tutaj:

  • Wszystkie akcje, które chcesz wykonać muszą być reprezentowane przez callables z ten sam podpis (na przykład, podpalacze, które nie przyjmują żadnych argumentów). Jeśli jeszcze nie są, zawiń je w małe funkcje, wyrażenia lambda, klasy wywoływalne, itp.
  • Klauzule except są złym pomysłem, ale prawdopodobnie już to wiesz.

Alternatywnym sposobem, który jest bardziej elastyczny, jest użycie funkcję wyższego rzędu takich jak

def logging_exceptions(f, *args, **kwargs): 
    try: 
     f(*args, **kwargs) 
    except Exception as e: 
     print("Houston, we have a problem: {0}".format(e)) 
+0

Tak, wiedziałem, dlatego włożyłem pierwszy z logowaniem mieszkańców w pytaniu. Jest to nowa rzecz, do której ostatnio się przyzwyczaiłem, aby zalogować moich mieszkańców, bardzo pomaga w debugowaniu rzeczy na serwerach produkcyjnych. Naprawdę podoba mi się sposób, w jaki tworzysz listę funkcji, chociaż w moim pytaniu to wszystko działa w moim prawdziwym przykładzie życia, większość z nich to także instrukcje i zapisujące zmienne. Ale może uda się to obejść. –

0
try: 
    this_may_cause_an_exception() 
except: 
    logging.exception('An error occured') 
finally: 
    but_I_still_wanna_run_this() 
    and_this() 
    and_also_this() 

można wykorzystywać blok finally obsługi wyjątków. W rzeczywistości jest przeznaczony do czyszczenia kodu.

EDIT: Widzę, że wszystkie funkcje mogą rzucać wyjątki, w których odpowiedź przypadku larsmans' jest o najczystszym mogę myśleć złapać wyjątek dla każdego wywołania funkcji.

+3

Co się stanie, jeśli wystąpi wyjątek w but_i_still_wanna_run_this() – shiva

+0

. Dlatego edytowałem post, aby uwzględnić fakt, że Sam chce przechwytywać wyjątki w każdej funkcji. –

+1

Po prostu usuń swój post, jeśli nie odpowiada poprawnie na pytanie ... – schlamar

2

Wpadłem na coś podobnego i zadałem pytanie na temat SO here. Zaakceptowana odpowiedź obsługuje rejestrowanie i obserwowanie tylko określonego wyjątku.Skończyło się ze zmodyfikowanej wersji:

class Suppressor: 
    def __init__(self, exception_type, l=None): 
     self._exception_type = exception_type 
     self.logger = logging.getLogger('Suppressor') 
     if l: 
      self.l = l 
     else: 
      self.l = {} 
    def __call__(self, expression): 
     try: 
      exec expression in self.l 
     except self._exception_type as e: 
      self.logger.debug('Suppressor: suppressed exception %s with content \'%s\'' % (type(self._exception_type), e)) 

użytkowa tak:

s = Suppressor(yourError, locals()) 
s(cmdString) 

Więc można skonfigurować listę poleceń i używać map z tłumika uruchomić we wszystkich z nich.

+3

Nigdy nie ustawiaj domyślnej wartości parametru na zmienną, taką jak 'List',' Dictionary' lub (w pewnych warunkach) 'object'. Ponieważ są one oceniane tylko raz i powodują poważne wycieki pamięci i błędy logiczne. http://docs.python.org/tutorial/controlflow.html#default-argument-values ​​ – FallenAngel

+0

użyj dekoratora zamiast ... – schlamar

+0

@FallenAngel Whoops! Nie mogę uwierzyć, że to zrobiłem, dzięki! –

0

można obsłużyć takiego zadania z dekoratora:

import logging 
from functools import wraps 

def log_ex(func): 
    @wraps(func) 
    def _wrapper(*args, **kwargs): 
     try: 
      func(*args, **kwargs) 
     except Exception: 
      logging.exception('...') 
    return _wrapper 

@log_ex 
def this_may_cause_an_exception(): 
    print 'this_may_cause_an_exception' 
    raise RuntimeError() 

@log_ex 
def but_i_wanna_run_this(): 
    print 'but_i_wanna_run_this' 

def test(): 
    this_may_cause_an_exception() 
    but_i_wanna_run_this() 

wywołaniu funkcji testowej będzie wyglądać (który pokaże, że obie funkcje były wykonywane):

>>> test() 
this_may_cause_an_exception 
ERROR:root:... 
Traceback (most recent call last): 
    File "<stdin>", line 5, in _wrapper 
    File "<stdin>", line 4, in my_func 
RuntimeError 
but_i_wanna_run_this 
+0

Co się stanie, jeśli linia w funkcji wywoła wyjątek, nie sądzę, że mogłaby ona wykonać następną linię w prawo? Ale zamiast tego wyjść z funkcji i zalogować wyjątek? –

+0

@SamStoelinga Nie, ale zostanie wywołana następna funkcja.Zaktualizowałem powyższą odpowiedź, aby była bardziej przejrzysta. Jest to podejście podobne do przyjętej, ale nie musisz tworzyć listy, a twoje funkcje nie muszą mieć stałego podpisu. – schlamar

+0

Ah tak, to było głupie ze mnie;) również ładnym sposobem obejścia, ale nadal powodowałoby mnie dużo kodu, myślę, że najlepszym rozwiązaniem byłoby mieć dekorator, który pozwoli na wykonanie całej funkcji bez względu na to, czy jakakolwiek linia funkcji wywołuje błąd, nie wiem, czy jest to możliwe w python. –

0

Czasem, gdy język tęskni za wspieraniem twojego eleganckiego sposobu wyrażania idei, ponieważ rozwój języka dosłownie zawiódł w ostatnich dekadach, możesz polegać tylko na tym, że Python wciąż jest językiem dynamicznym, który obsługuje instrukcję exec, co umożliwia:

code=""" 
for i in range(Square_Size): 
    Square[i,i] @= 1 
    Square[i+1,i] @= 2 
    @dowhatever() 
""" 

Ten nowy operator czyni kod bardziej pythonic i elegancki, ponieważ nie ma potrzeby określania dodatkowych IF-statemens, które gwarantują, że wskaźnik pozostanie w bound lub funkcja ma odnieść sukces, który jest całkowicie bez znaczenia dla tego, co chcemy express (to po prostu nie powinno się zatrzymywać) tutaj (uwaga: podczas gdy bezpieczne indeksowanie byłoby możliwe poprzez stworzenie klasy opartej na klasie list, operator ten działa zawsze, gdy powinno być próby catch), w Lisp byłoby to łatwe do zdefiniowania w sposób Lispy, ale nie da się go zdefiniować w elegancki sposób w Pythonie, ale wciąż jest mały preparator, który umożliwi: exec "\n".join([o+"try: "+z.replace("@","")+"\n"+o+"except: pass" if "@" in z else z for z in code.split("\n") for o in ["".join([h for h in z if h==" "])]]) #new <- hackish operator which wraps try catch into line

Wynik, assumin g że plac 4x4 zawierały tylko zera:

[1 0 0 0] 
[2 1 0 0] 
[0 2 1 0] 
[0 0 2 1] 

odpowiednio: Sage/Sagemath CAS wykorzystuje preparse-funkcję który przekształca kodu, zanim dotrze do interpretera. Małpa-patch dla tej funkcji będzie:

def new_preparse(code,*args, **kwargs): 
    code="\n".join([o+"try: "+z.replace("@","")+"\n"+o+"except: pass" if "@" in z else z for z in code.split("\n") for o in ["".join([h for h in z if h==" "])]]) 
    return preparse(code) 
sage.misc.preparser.preparse=new_preparse 
Powiązane problemy