[python] “제외 : 통과”가 나쁜 프로그래밍 관행 인 이유는 무엇입니까?

종종 사용 except: pass하지 않는 방법에 대한 다른 Stack Overflow 질문에 대한 의견이 있습니다 . 왜 이것이 나쁜가요? 때로는 오류가 무엇인지 신경 쓰지 않고 코드를 계속 사용하고 싶습니다.

try:
    something
except:
    pass

except: pass블록을 사용하는 것이 좋지 않습니까? 무엇이 나쁜가요? 내가 사실이다 pass오류 또는 내가 그 except오류?



답변

잡기 : 올바르게 추측, 그것은 두 가지 측면이 있습니다 어떤 후 예외 유형을 지정하지 의해 오류를 except, 단순히 어떤 조치를 취하기없이 통과가.

내 설명은 “약간”길다. 그래서 tl; dr 다음과 같이 분류된다.

  1. 캐치하지 마십시오 어떤 오류를 . 항상 복구 할 예외를 지정하고 예외 만 포착하십시오.
  2. 블록을 제외하고는지나 가지 않도록하십시오 . 명시 적으로 원하지 않는 한 이것은 일반적으로 좋은 신호가 아닙니다.

그러나 자세하게 살펴 보자.

캐치하지 마십시오 어떤 오류를

try블록을 사용할 때 예외가 발생할 가능성이 있다는 것을 알고 있기 때문에 일반적으로이 작업을 수행합니다. 따라서, 당신은 또한 무엇이 깨질 수 있고 어떤 예외가 던져 질 수 있는지에 대한 대략적인 아이디어를 가지고 있습니다. 이러한 경우, 긍정적으로 복구 할 수 있기 때문에 예외가 발생 합니다. 즉, 귀하는 예외에 대비하고 해당 예외에 대비하여 대체 계획을 마련해야합니다.

예를 들어, 사용자에게 숫자를 입력하도록 요청하면을 int()올릴 수있는를 사용하여 입력을 변환 할 수 있습니다 ValueError. 사용자에게 간단히 다시 시도하도록 요청하여이를 쉽게 복구 할 수 있으므로 사용자를 파악하고 ValueError다시 프롬프트하면 적절한 계획이됩니다. 다른 예는 파일에서 일부 구성을 읽으려는 경우 해당 파일이 존재하지 않는 경우입니다. 구성 파일이므로 대체로 일부 기본 구성이있을 수 있으므로 파일이 꼭 필요한 것은 아닙니다. 따라서 FileNotFoundError기본 구성을 간단히 적용하고 적용하는 것이 좋습니다. 이제 두 경우 모두, 우리는 우리가 기대하는 매우 특정한 예외를 가지고 있으며, 그것으로부터 회복 할 동등한 계획이 있습니다. 이와 같이, 각각의 경우에, 우리는 만 명시 적으로 except 특정이 예외.

그러나 우리가 모든 것을 잡아야한다면, 우리가 회복 할 준비가 된 예외 외에, 우리가 예상하지 못했던 예외가 생겨 실제로 회복 할 수없는 기회도있을 것입니다. 또는 회복해서는 안됩니다.

구성 파일 예제를 위에서 보자. 누락 된 파일의 경우 기본 구성을 적용한 후 나중에 자동으로 구성을 저장하기로 결정했을 수 있습니다 (다음에 파일이 존재 함). 이제 우리는 IsADirectoryError또는PermissionError대신에. 그러한 경우, 우리는 아마도 계속하고 싶지 않을 것입니다. 기본 구성을 계속 적용 할 수는 있지만 나중에 파일을 저장할 수 없습니다. 그리고 사용자가 사용자 정의 구성을 가지고 있었기 때문에 기본값을 사용하지 않는 것이 좋습니다. 따라서 사용자에게 즉시 알리고 프로그램 실행도 중단하려고합니다. 그러나 그것은 우리가 작은 코드 부분 내에서 깊게하고 싶은 것이 아닙니다. 이는 응용 프로그램 수준에서 중요하므로 맨 위에서 처리해야합니다. 따라서 예외가 발생하도록합니다.

또 다른 간단한 예는 Python 2 관용구 문서 에도 언급되어 있습니다. 여기에는 코드에 간단한 오타가있어 코드가 중단됩니다. 우리는 모든 예외를 잡기 때문에 NameErrorsSyntaxErrors 도 잡습니다 . 둘 다 프로그래밍하는 동안 우리 모두에게 발생하는 실수입니다. 그리고 둘 다 코드를 배송 할 때 절대 포함하고 싶지 않은 실수입니다. 그러나 우리는 또한 그것들을 잡았 기 때문에 그것들이 발생했다는 것을 알지 못하고 올바르게 디버깅하는 데 도움이 없습니다.

그러나 우리가 준비하지 않은 더 위험한 예외도 있습니다. 예를 들어 SystemError 는 일반적으로 거의 발생하지 않으며 실제로 계획 할 수없는 것입니다. 그것은 더 복잡한 일이 진행되고 있음을 의미하며, 현재 진행중인 작업을 계속하지 못할 수도 있습니다.

어쨌든 코드의 작은 부분으로 모든 것을 준비 할 가능성이 거의 없으므로 실제로는 예외를 잡아야합니다. 어떤 사람들은 적어도 캐치에 제안 Exception이 같은 것들을 포함되지 않습니다으로 SystemExit하고 KeyboardInterrupt있는 디자인으로 응용 프로그램을 종료 할 수 있습니다,하지만 난이 여전히 너무 불특정 것을 주장 할 것이다. 나는 개인적으로 잡기 동의를 하나의 장소가 Exception단지 또는 임의예외는 단일 전역 응용 프로그램 수준 예외 처리기에 있으며 준비되지 않은 예외를 기록하는 단일 목적이 있습니다. 이렇게하면 예기치 않은 예외에 대한 정보를 여전히 많이 보유 할 수 있습니다. 그런 다음 코드를 확장하여 명시 적으로 (복구 할 수있는 경우) 처리하거나 버그가있는 경우이를 처리하여 테스트 사례를 작성하여 확인할 수 있습니다. 다시는 발생하지 않습니다. 그러나 물론 우리가 이미 기대했던 예외를 잡은 경우에만 작동하므로 예상하지 않은 예외는 자연스럽게 발생합니다.

블록을 제외하고는지나 가지 않도록하십시오

소수의 특정 예외를 명시 적으로 포착 할 때 단순히 아무 것도하지 않아도되는 상황이 많이 있습니다. 그런 경우에는 그냥있는 except SomeSpecificException: pass것이 좋습니다. 그러나 대부분의 경우 복구 프로세스와 관련된 코드가 필요할 가능성이 높기 때문에 그렇지 않습니다 (위에서 언급 한 것처럼). 예를 들어 작업을 다시 시도하거나 기본값을 설정하는 것일 수 있습니다.

예를 들어 코드가 성공할 때까지 반복하도록 이미 구성되어 있기 때문에 그렇지 않은 경우 전달하는 것으로 충분합니다. 위의 예를 살펴보면 사용자에게 숫자를 입력하도록 요청할 수 있습니다. 우리는 사용자가 요청한 것을하지 않기를 원하기 때문에 처음에는 루프에 넣을 수 있으므로 다음과 같이 보일 수 있습니다.

def askForNumber ():
    while True:
        try:
            return int(input('Please enter a number: '))
        except ValueError:
            pass

예외가 발생하지 않을 때까지 계속 시도하기 때문에 except 블록에서 특별한 작업을 수행 할 필요가 없으므로 괜찮습니다. 그러나 물론 사용자에게 입력을 반복 해야하는 이유를 알려주는 오류 메시지를 표시하고 싶다고 주장 할 수 있습니다.

다른 많은 경우에, 그냥 전달하는 except것은 우리가 잡는 예외에 대해 실제로 준비되지 않았다는 신호입니다. 이러한 예외가 간단 ValueError하거나 (와 같이) 또는 TypeError통과 할 수있는 이유가 명백하지 않은 경우 통과하지 않도록 노력하십시오. 정말로 할 일이 없다면 (그리고 당신은 그것에 대해 절대적으로 확신한다면), 왜 그런지 설명을 추가하는 것을 고려하십시오; 그렇지 않으면, 복구 블록을 실제로 포함하도록 except 블록을 확장하십시오.

except: pass

최악의 범죄자는 둘 다의 조합입니다. 우리가 기꺼이 잡는 것을이 의미 있는 우리가 절대적으로 준비되지 않더라도 오류 우리는 또한 그것에 대해 아무것도 할 수 없습니다. 당신은 적어도 오류를 기록 할도 가능성이 여전히 응용 프로그램을 종료 할 리 레이즈 (당신이 MemoryError의 후 정상처럼 계속 확률이 낮다). 그냥 통과하면 응용 프로그램을 어느 정도 살아있게 유지할뿐만 아니라 (물론 잡는 위치에 따라) 모든 정보를 버려서 오류를 발견하는 것이 불가능합니다.


결론은 다음과 같습니다. 실제로 예상하고 복구 할 준비가 된 예외 만 포착하십시오. 다른 모든 것들은 당신이 고쳐야 할 실수이거나 어쨌든 준비되지 않은 것일 수 있습니다. 특정 전달실제로 예외를 수행 할 필요가없는 경우 예외를 하는 것이 좋습니다. 다른 모든 경우에, 그것은 단지 추정과 게으름의 표시 일뿐입니다. 그리고 당신은 분명히 그것을 고치고 싶습니다.


답변

여기서 주요 문제는 모든 오류를 무시한다는 것입니다. 메모리 부족, CPU 굽기, 사용자 중지, 프로그램 종료, Jabberwocky가 사용자를 죽이고 있습니다.

이것은 너무 길다. 당신은 “이 네트워크 오류를 무시하고 싶습니다”라고 생각합니다. 예기치 않은 문제가 발생 하면 코드가 자동으로 계속 진행되어 아무도 예측할 수없는 완전히 예측할 수없는 방식으로 중단됩니다.

따라서 일부 오류 만 무시하고 나머지는 통과하도록 제한해야합니다.


답변

의사 코드를 실행 해도 문자 그대로 오류가 발생하지 않습니다.

try:
    something
except:
    pass

마치을 던지는 대신 완벽하게 유효한 코드 조각 인 것처럼 NameError. 이것이 당신이 원하는 것이 아니길 바랍니다.


답변

“제외 : 통과”가 나쁜 프로그래밍 관행 인 이유는 무엇입니까?

왜 이것이 나쁜가요?

try:
    something
except:
    pass

이것은 포함, 가능한 모든 예외를 catch GeneratorExit, KeyboardInterrupt그리고 SystemExit– 당신은 아마 캐치하지 않으려는 예외한다. 잡기와 동일합니다 BaseException.

try:
    something
except BaseException:
    pass

이전 버전의 설명서는 다음 과 같이 말합니다 .

Python의 모든 오류는 예외를 발생 except:시키기 때문에를 사용 하면 많은 프로그래밍 오류를 런타임 문제처럼 보이게하여 디버깅 프로세스를 방해 할 수 있습니다.

파이썬 예외 계층

부모 예외 클래스를 잡으면 모든 자식 클래스도 잡습니다. 처리 할 수있는 예외 만 잡는 것이 훨씬 더 우아합니다.

다음은 Python 3 예외 계층 구조입니다. 정말로 모두 잡으시겠습니까? :

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
           +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

이러지마

이 형태의 예외 처리를 사용하는 경우 :

try:
    something
except: # don't just do a bare except!
    pass

그런 다음 somethingCtrl-C로 블록 을 중단 할 수 없습니다 . 귀하의 프로그램은 내부의 가능한 모든 예외를 간과합니다try 코드 블록 .

다음은 바람직하지 않은 동작을하는 또 다른 예입니다.

except BaseException as e: # don't do this either - same as bare!
    logging.info(e)

대신, 당신이 찾고있는 특정 예외 만 잡으려고 노력하십시오. 예를 들어 전환시 가치 오류가 발생할 수 있음을 알고있는 경우 :

try:
    foo = operation_that_includes_int(foo)
except ValueError as e:
    if fatal_condition(): # You can raise the exception if it's bad,
        logging.info(e)   # but if it's fatal every time,
        raise             # you probably should just not catch it.
    else:                 # Only catch exceptions you are prepared to handle.
        foo = 0           # Here we simply assign foo to 0 and continue. 

다른 예를 가진 추가 설명

웹 스크래핑을하고 있고 UnicodeError 있기 때문에 그렇게 할 수 있지만, 가장 광범위한 예외 잡기를 사용했기 때문에 다른 근본적인 결함이있을 수있는 코드가 대역폭을 낭비하려고합니다. , 처리 시간, 장비 마모, 메모리 부족, 가비지 데이터 수집 등

다른 사람들이 귀하의 코드에 의존 할 수 있도록 완료하도록 요청하는 경우 모든 것을 처리해야한다는 느낌을받습니다. 그러나 개발하면서 소음에 시달리게 실패한다면 간헐적으로 발생할 수있는 문제를 해결할 수있는 기회가 있지만 장기적으로 많은 비용이 드는 버그 일 것입니다.

보다 정확한 오류 처리를 통해 코드를 더욱 강력하게 만들 수 있습니다.


답변

>>> import this

파이썬의 선 (The Zen of Python), Tim Peters

못생긴 것보다 아름답습니다.
암시적인 것보다 명시적인 것이 좋습니다.
단순함이 복잡한 것보다 낫습니다.
복잡한 것이 복잡한 것보다 낫습니다.
평평한 것이 중첩보다 낫습니다.
스파 스가 밀도보다 낫습니다.
가독성이 중요합니다.
특별한 경우는 규칙을 어길만큼 특별하지 않습니다.
실용성은 순도를 능가하지만.
오류가 자동으로 전달되지 않아야합니다.
명시 적으로 침묵하지 않는 한.
모호함에 직면하여 추측하려는 유혹을 거부하십시오.
그것을하는 명백한 방법이 있어야합니다.
네덜란드 인이 아니라면 처음에는 그 방법이 명확하지 않을 수 있습니다. 바로 지금
지금보다 결코 낫습니다.
결코 결코 낫지 않지만
구현이 설명하기 어렵다면 나쁜 생각입니다.
구현이 설명하기 쉬운 경우 좋은 생각 일 수 있습니다.
네임 스페이스는 훌륭한 아이디어 중 하나입니다. 더 많은 것을 해보자!

자, 여기 내 의견이 있습니다. 오류를 발견 할 때마다 처리 할 작업을 수행해야합니다 (예 : 로그 파일이나 다른 것에 작성). 적어도 오류가 발생했음을 알려줍니다.


답변

당신은 적어도 사용해야합니다 except Exception:같은 시스템 예외를 잡는 피하기 위해 SystemExit또는 KeyboardInterrupt. 문서에 대한 링크 입니다.

일반적으로 원하지 않는 예외를 포착하지 않도록 명시 적으로 포착하려는 예외를 정의해야합니다. 어떤 예외를 무시 해야하는지 알아야합니다 .


답변

첫째, 그것은 Zen of Python 의 두 가지 원칙을 위반합니다 .

  • 암시 적보다 묵시적이다
  • 오류는 조용히지나 가지 않아야합니다

의미하는 것은 의도적으로 오류를 자동으로 전달한다는 것입니다. 또한, 정확히 어떤 오류가 발생했는지 알 수 없습니다.except: pass 예외를 잡을 수 .

둘째, 우리가 파이썬의 선 (Zen of Python)에서 벗어나려고 시도하고, 단지 온전한 용어로 말하면, 사용 except:pass하면 시스템에 대한 지식과 통제력이 없어진다는 것을 알아야 합니다. 경험상 오류가 발생하면 예외를 발생시키고 적절한 조치를 취하는 것입니다. 미리 알지 못하는 경우 어떤 조치를 취해야하는지 적어도 어딘가에 오류를 기록하십시오 (예외를 개선하는 것이 좋습니다).

try:
    something
except:
    logger.exception('Something happened')

그러나 일반적으로 예외를 잡으려고하면 뭔가 잘못한 것일 수 있습니다.